Compare commits

...

9 commits
v1.2.0 ... dev

Author SHA1 Message Date
Alejandro Asenjo Nitti
42646e2cb1
Add discord banner embed (#668)
Some checks failed
validate-internal / build (push) Has been cancelled
2025-10-04 20:39:51 -04:00
Wiseguy
54950a1040
1.2.2 (#652)
Some checks failed
validate-internal / build (push) Has been cancelled
* Update RT64 for multimonitor frame pacing issue and bump version number

* Add community discord server link to readme

* Bump version number to 1.2.2
2025-08-26 20:28:41 -04:00
Wiseguy
24704d86f1
1.2.1 (#649)
Some checks failed
validate-internal / build (push) Has been cancelled
2025-08-18 17:21:15 -04:00
Wiseguy
b5360b0546
1.2.1 Changes (#625)
Some checks failed
validate-internal / build (push) Has been cancelled
* Update runtime for hook and callback sorting, update version number

* Actually update version number

* Automatically open mods menu when dragging a mod which also prevents issues when installing a mod from the launcher menu

* Update RT64 to add texture pack shift configuration and fix minimized memory leak

* Update runtime for return hook getter exports

* Actually update rt64

* Update RT64 to fix texture pack ordering

* Implement optional dependencies, fix memory slotmaps, bump version number to 1.2.1-dev

* Add command-line option to show console output on Windows. (#632)

* Add new raphnet adapter revision to controller DB

* Add another mayflash N64 adapter to the controller database file

* Update runtime after merge for optional dependencies

* Update runtime for optional dependency mod callback fix

* Add mayflash magic NS to controller database

* Update RT64 for extended address fix and x11 dependency removal

* Update RT64 to fix build issue caused by x11

* Update runtime to remove unnnecessary x11 includes

* Fix more x11 define compilation issues

* Fix the x86-64 CPU requirement listing in the readme (#634)

* Transform tagging for keaton grass tornado

* Interpolation for sword trails

* Switch RT64 to gEXVertex fix branch (temporary until merge)

* Add 8bitdo 64 bluetooth controller to database

* Add export to get bowstring transform ID

* Update RT64 to fix linux dev mode menu and texture streaming race condition

* Fix all the interpolation glitches in the Gibdo Mask cutscene (#641)

* Fix the actor extension API breaking when registering an extension for actor type 0 first

* Remove slotmap submodule and integrate header directly after submodule URL changed

* Transform tagging for ObjGrass

* Adding autosave events. (#611)

* Adding autosave events. Dead simple.

* Update autosaving.c to include @recomp_event comments

* Prevent autosaves during minigames and fix holding powder keg on autosave load (#640)

* Update runtime for more accurate VI and switch to improved pacing RT64 branch (#644)

* Update runtime for more accurate VI and switch to improved pacing RT64 branch

* Update N64ModernRuntime and RT64 after merge

* Update recompiler to match runtime symbol list

* Remove unused gibdo patch file

---------

Co-authored-by: Darío <dariosamo@gmail.com>
Co-authored-by: Reonu <15913880+Reonu@users.noreply.github.com>
2025-08-15 21:31:57 -04:00
DoctorDink
46d9e92dda
Prevent autosaves during minigames and fix holding powder keg on autosave load (#640)
Some checks failed
validate-internal / build (push) Has been cancelled
2025-08-11 21:46:38 -04:00
Wiseguy
a215103002
Remove slotmap submodule and integrate header directly after submodule URL changed (#645)
Some checks failed
validate-internal / build (push) Has been cancelled
2025-08-10 18:33:09 -04:00
Manuel Alfayate Corchete
bb7030bcd1
Remove X11 usage from CMakeLists (#633)
Some checks failed
validate-internal / build (push) Has been cancelled
2025-07-25 14:54:43 -04:00
LT_SCHMIDDY
0bc0fd2610
Adding autosave events. (#611)
Some checks failed
validate-internal / build (push) Has been cancelled
* Adding autosave events. Dead simple.

* Update autosaving.c to include @recomp_event comments
2025-06-18 21:34:32 -04:00
Wiseguy
54997438de Update readme for randomizer link and portable mode
Some checks failed
validate-internal / build (push) Has been cancelled
2025-05-04 13:25:16 -04:00
28 changed files with 2724 additions and 61 deletions

View file

@ -9,7 +9,7 @@ on:
N64RECOMP_COMMIT:
type: string
required: false
default: '989a86b36912403cd323de884bf834f2605ea770'
default: 'a13e5cff96686776b0e03baf23923e3c1927b770'
DXC_CHECKSUM:
type: string
required: false

4
.gitmodules vendored
View file

@ -19,6 +19,4 @@
[submodule "Zelda64RecompSyms"]
path = Zelda64RecompSyms
url = https://github.com/Zelda64Recomp/Zelda64RecompSyms
[submodule "lib/slot_map"]
path = lib/slot_map
url = https://github.com/SergeyMakeev/slot_map

View file

@ -222,7 +222,7 @@ target_include_directories(Zelda64Recompiled PRIVATE
${CMAKE_SOURCE_DIR}/lib/rt64/src/render
${CMAKE_SOURCE_DIR}/lib/freetype-windows-binaries/include
${CMAKE_SOURCE_DIR}/lib/rt64/src/contrib/nativefiledialog-extended/src/include
${CMAKE_SOURCE_DIR}/lib/slot_map/slot_map
${CMAKE_SOURCE_DIR}/lib/SlotMap
${CMAKE_BINARY_DIR}/shaders
${CMAKE_CURRENT_BINARY_DIR}
)
@ -291,7 +291,7 @@ if (WIN32)
)
target_sources(Zelda64Recompiled PRIVATE ${CMAKE_SOURCE_DIR}/icons/app.rc)
target_link_libraries(Zelda64Recompiled PRIVATE SDL2)
target_link_libraries(Zelda64Recompiled PRIVATE SDL2 Winmm.lib)
endif()
if (APPLE)
@ -309,8 +309,6 @@ endif()
if (CMAKE_SYSTEM_NAME MATCHES "Linux")
find_package(SDL2 REQUIRED)
find_package(X11 REQUIRED)
add_compile_definitions("RT64_SDL_WINDOW_VULKAN")
# Generate icon_bytes.c from the app icon PNG.
@ -325,14 +323,6 @@ if (CMAKE_SYSTEM_NAME MATCHES "Linux")
target_include_directories(Zelda64Recompiled PRIVATE ${SDL2_INCLUDE_DIRS})
message(STATUS "X11_FOUND = ${X11_FOUND}")
message(STATUS "X11_Xrandr_FOUND = ${X11_Xrandr_FOUND}")
message(STATUS "X11_INCLUDE_DIR = ${X11_INCLUDE_DIR}")
message(STATUS "X11_LIBRARIES = ${X11_LIBRARIES}")
target_include_directories(Zelda64Recompiled PRIVATE ${X11_INCLUDE_DIR} ${X11_Xrandr_INCLUDE_PATH})
target_link_libraries(Zelda64Recompiled PRIVATE ${X11_LIBRARIES} ${X11_Xrandr_LIB})
find_package(Freetype REQUIRED)
message(STATUS "FREETYPE_FOUND = ${FREETYPE_FOUND}")

View file

@ -3,6 +3,10 @@ Zelda 64: Recompiled is a project that uses [N64: Recompiled](https://github.com
### [Check out the latest release here](https://github.com/Mr-Wiseguy/Zelda64Recomp/releases/latest).
Join the [N64: Recompiled Community Discord](https://discord.gg/AWZThJ4dPf) to discuss this and other N64: Recompiled projects!
[![Discord Invitation](https://discordapp.com/api/guilds/1374083583739826328/widget.png?style=banner2 'N64 Recomp')](https://discord.gg/AWZThJ4dPf)
### **This repository and its releases do not contain game assets. The original game is required to build or run this project.**
<div align="left" valign="middle">
@ -48,7 +52,7 @@ A GPU supporting Direct3D 12.0 (Shader Model 6), Vulkan 1.2, or Metal Argument B
* Intel HD 510 (Skylake)
* A Mac with Apple Silicon or an Intel 7th Gen CPU with MacOS 13.0+
On x86-64 PCs, a CPU supporting the AVX instruction set is also required (Intel Core 2000 series or AMD Bulldozer and newer). ARM64 builds will work on any ARM64 CPU.
On x86-64 PCs, a CPU supporting the SSE4.1 instruction set is also required (Intel Core 2 Penryn series or AMD Bulldozer and newer). ARM64 builds will work on any ARM64 CPU.
If you have issues with crashes on startup, make sure your graphics drivers are fully up to date.
@ -136,7 +140,10 @@ You'll probably also want to change the default behavior so that you don't need
Instead, you can change the game by installing mods. See the [mod support](#mod-support) section for details.
#### Does this project have a randomizer?
Yes, there is a randomizer available as a mod for this project which can be found on the thunderstore page linked in [mod support](#mod-support).
Yes, there is a randomizer available as a mod for this project which can be found at https://github.com/RecompRando/MMRecompRando/releases/latest. Simply download MMRecompRando.zip from the releases and install it like any other mod.
#### Can you run this project as a portable application?
Yes, if you place a file named `portable.txt` in the same folder as the executable then this project will run in portable mode. In portable mode, the save files, config files, and mods are placed in the same folder as the executable.
## Known Issues
* Overlays such as MSI Afterburner and other software such as Wallpaper Engine can cause performance issues with this project that prevent the game from rendering correctly. Disabling such software is recommended.

View file

@ -27,7 +27,7 @@ namespace zelda64 {
void enable_instant_present() override;
void send_dl(const OSTask *task) override;
void update_screen(uint32_t vi_origin) override;
void update_screen() override;
void shutdown() override;
uint32_t get_display_framerate() const override;
float get_resolution_scale() const override;

View file

@ -7,7 +7,8 @@
"project": "CMakeLists.txt",
"projectTarget": "Zelda64Recompiled.exe",
"name": "Zelda64Recompiled.exe",
"currentDir": "${workspaceRoot}"
"currentDir": "${workspaceRoot}",
"args": ["--show-console"]
}
]
}

@ -1 +1 @@
Subproject commit c5e268aa0f71cf06a10a001da981dc3e02e7dff0
Subproject commit df7e820d8c55e4fcb4616c210cbb2c01b25cd48c

257
lib/SlotMap/README.md Normal file
View file

@ -0,0 +1,257 @@
This file is originally from the repo: https://github.com/SergeyMakeev/SlotMap
The original license and README are as follows:
```
MIT License
Copyright (c) 2022 Sergey Makeev
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
```
# Slot Map
[![Actions Status](https://github.com/SergeyMakeev/slot_map/workflows/build/badge.svg)](https://github.com/SergeyMakeev/slot_map/actions)
[![Build status](https://ci.appveyor.com/api/projects/status/i00kv17e3ia5jr7q?svg=true)](https://ci.appveyor.com/project/SergeyMakeev/slot-map)
[![codecov](https://codecov.io/gh/SergeyMakeev/slot_map/branch/main/graph/badge.svg?token=3GRAFTRYQU)](https://codecov.io/gh/SergeyMakeev/slot_map)
![MIT](https://img.shields.io/badge/license-MIT-blue.svg)
A Slot Map is a high-performance associative container with persistent unique keys to access stored values. Upon insertion, a key is returned that can be used to later access or remove the values. Insertion, removal, and access are all guaranteed to take `O(1)` time (best, worst, and average case)
Great for storing collections of objects that need stable, safe references but have no clear ownership.
The difference between a `std::unordered_map` and a `dod::slot_map` is that the slot map generates and returns the key when inserting a value. A key is always unique and will only refer to the value that was inserted.
Usage example:
```cpp
slot_map<std::string> strings;
auto red = strings.emplace("Red");
auto green = strings.emplace("Green");
auto blue = strings.emplace("Blue");
const std::string* val1 = strings.get(red);
if (val1)
{
printf("red = '%s'\n", val1->c_str());
}
strings.erase(green);
printf("%d\n", strings.has(green));
printf("%d\n", strings.has(blue));
```
Output:
```
red = 'Red'
0
1
```
# Implementation details
The slot map container will allocate memory in pages (default page size = 4096 elements) to avoid memory spikes during growth and be able to deallocate pages that are no longer needed.
Also, the page-based memory allocator is very important since it guarantees "pointers stability"; hence, we never move values in memory.
Keys are always uses `uint64_t/uint32_t` (configurable) and technically typless, but we "artificially" make them typed to get a few extra compile-time checks.
i.e., the following code will produce a compiler error
```cpp
slot_map<std::string> strings;
slot_map<int> numbers;
slot_map<int>::key numKey = numbers.emplace(3);
const std::string* value = strings.get(numKey); // <---- can not use slot_map<int>::key to index slot_map<std::string> !
```
The keys can be converted to/from their numeric types if you do not need additional type checks.
```cpp
slot_map<int> numbers;
slot_map<int>::key numKey = numbers.emplace(3);
uint64_t rawKey = numKey; // convert to numeric type (like cast pointer to void*)
...
slot_map<int>::key numKey2{rawKey}; // create key from numeric type
```
When a slot is reused, its version is automatically incremented (to invalidate all existing keys that refers to the same slot).
But since we only use 20-bits *(10-bits for 32 bit keys)* for version counter, there is a possibility that the version counter will wrap around,
and a new item will get the same key as a removed item.
To mitigate this potential issue, once the version counter overflows, we disable that slot so that no new keys are returned for this slot
(this gives us a guarantee that there are no key collisions)
To prevent version overflow from happening too often, we need to ensure that we don't reuse the same slot too often.
So we do not reuse recently freed slot-indices as long as their number is below a certain threshold (`kMinFreeIndices = 64`).
Keys also can carry a few extra bits of information provided by a user that we called `tag`.
That might be handy to add application-specific data to keys.
For example:
```cpp
slot_map<std::string> strings;
auto red = strings.emplace("Red");
red.set_tag(13);
auto tag = red.get_tag();
assert(tag == 13);
```
Here is how a key structure looks like internally
64-bit key type
| Component | Number of bits |
| ---------------|------------------------|
| tag | 12 |
| version | 20 (0..1,048,575 |
| index | 32 (0..4,294,967,295) |
32-bit key type
| Component | Number of bits |
| ---------------|---------------------|
| tag | 2 |
| version | 10 (0..1023) |
| index | 20 (0..1,048,575) |
Note: To use your custom memory allocator define `SLOT_MAP_ALLOC`/`SLOT_MAP_FREE` before including `"slot_map.h"`
```cpp
#define SLOT_MAP_ALLOC(sizeInBytes, alignment) aligned_alloc(alignment, sizeInBytes)
#define SLOT_MAP_FREE(ptr) free(ptr)
#include "slot_map.h"
```
# API
`bool has_key(key k) const noexcept`
Returns true if the slot map contains a specific key
`void reset()`
Clears the slot map and releases any allocated memory.
Note: By calling this function, you must guarantee that no handles are in use!
Otherwise calling this function might be dangerous and lead to key "collisions".
You might consider using "clear()" instead.
`void clear()`
Clears the slot map but keeps the allocated memory for reuse.
Automatically increases version for all the removed elements (the same as calling "erase()" for all existing elements)
`const T* get(key k) const noexcept`
If key exists returns a const pointer to the value corresponding to the given key or returns null elsewere.
`T* get(key k)`
If key exists returns a pointer to the value corresponding to the given key or returns null elsewere.
`key emplace(Args&&... args)`
Constructs element in-place and returns a unique key that can be used to access this value.
`void erase(key k)`
Removes element (if such key exists) from the slot map.
`std::optional<T> pop(key k)`
Removes element (if such key exists) from the slot map, returning the value at the key if the key was not previously removed.
`bool empty() const noexcept`
Returns true if the slot map is empty.
`size_type size() const noexcept`
Returns the number of elements in the slot map.
`void swap(slot_map& other) noexcept`
Exchanges the content of the slot map by the content of another slot map object of the same type.
`slot_map(const slot_map& other)`
Copy constructor
`slot_map& operator=(const slot_map& other)`
Copy assignment
`slot_map(slot_map&& other) noexcept`
Move constructor
`slot_map& operator=(slot_map&& other) noexcept`
Move asignment
`const_values_iterator begin() const noexcept`
`const_values_iterator end() const noexcept`
Const values iterator
```cpp
for (const auto& value : slotMap)
{
...
}
```
`Items items() const noexcept`
Const key/value iterator
```cpp
for (const auto& [key, value] : slotMap.items())
{
...
}
```
# References
Sean Middleditch
Data Structures for Game Developers: The Slot Map, 2013
https://web.archive.org/web/20180121142549/http://seanmiddleditch.com/data-structures-for-game-developers-the-slot-map/
Niklas Gray
Building a Data-Oriented Entity System (part 1), 2014
http://bitsquid.blogspot.com/2014/08/building-data-oriented-entity-system.html
Noel Llopis
Managing Data Relationships, 2010
https://gamesfromwithin.com/managing-data-relationships
Stefan Reinalter
Adventures in data-oriented design - Part 3c: External References, 2013
https://blog.molecular-matters.com/2013/07/24/adventures-in-data-oriented-design-part-3c-external-references/
Niklas Gray
Managing Decoupling Part 4 - The ID Lookup Table, 2011
https://bitsquid.blogspot.com/2011/09/managing-decoupling-part-4-id-lookup.html
Sander Mertens
Making the most of ECS identifiers, 2020
https://ajmmertens.medium.com/doing-a-lot-with-a-little-ecs-identifiers-25a72bd2647
Michele Caini
ECS back and forth. Part 9 - Sparse sets and EnTT, 2020
https://skypjack.github.io/2020-08-02-ecs-baf-part-9/
Andre Weissflog
Handles are the better pointers, 2018
https://floooh.github.io/2018/06/17/handles-vs-pointers.html
Allan Deutsch
C++Now 2017: "The Slot Map Data Structure", 2017
https://www.youtube.com/watch?v=SHaAR7XPtNU
Jeff Gates
Init, Update, Draw - Data Arrays, 2012
https://greysphere.tumblr.com/post/31601463396/data-arrays
Niklas Gray
Data Structures Part 1: Bulk Data, 2019
https://ourmachinery.com/post/data-structures-part-1-bulk-data/

1366
lib/SlotMap/slot_map.h Normal file

File diff suppressed because it is too large Load diff

@ -1 +1 @@
Subproject commit ada6cc62c421b142d9d90154765e44348115bd9e
Subproject commit 23cab603c4f9f4a8b369b38e036f1aa484603878

@ -1 +0,0 @@
Subproject commit b8ac8ebd89aa1cd18f20ce6e4ad1cac716f1933f

View file

@ -70,7 +70,13 @@ void Sram_SyncWriteToFlash(SramContext* sramCtx, s32 curPage, s32 numPages);
void recomp_reset_autosave_timer();
void recomp_reset_autosave_timer_slow();
RECOMP_DECLARE_EVENT(recomp_on_autosave(PlayState* play));
RECOMP_DECLARE_EVENT(recomp_after_autosave(PlayState* play));
RECOMP_EXPORT void recomp_do_autosave(PlayState* play) {
// @recomp_event recomp_on_autosave(PlayState* play): Autosave triggered.
recomp_on_autosave(play);
// Transfer the scene flags into the cycle flags.
Play_SaveCycleSceneFlags(&play->state);
// Transfer the cycle flags into the save buffer. Logic copied from func_8014546C.
@ -97,6 +103,9 @@ RECOMP_EXPORT void recomp_do_autosave(PlayState* play) {
Sram_SyncWriteToFlash(sramCtx, gFlashOwlSaveStartPages[fileNum * 2 + 1], gFlashOwlSaveNumPages[fileNum * 2 + 1]);
gSaveContext.save.isOwlSave = false;
// @recomp_event recomp_on_autosave(PlayState* play): Autosave finished.
recomp_after_autosave(play);
}
bool loading_deletes_owl_save = true;
@ -401,7 +410,7 @@ void autosave_post_play_update(PlayState* play) {
OSTime time_now = osGetTime();
// Check the following conditions:
// Check the following conditions for autosave safety:
// * The UI is in a normal state.
// * Time is passing.
// * No message is on screen.
@ -409,6 +418,10 @@ void autosave_post_play_update(PlayState* play) {
// * No cutscene is running.
// * The game is not in cutscene mode.
// * The clock has not reached the final 3 hours.
// * The player is not in an active/inactive minigame (not all minigames use this flag, default is STATUS_END)
// * The player is not in a timed minigame in the first set (Shooting Gallery, Butler Race, Spirit House, etc)
// * The player is not in a timed minigame in the second set (Goron Race, Treasure Game, Beaver Bros, etc)
// * The player is not taking a boat cruise
// * The player is allowed to pause.
if (gSaveContext.hudVisibility == HUD_VISIBILITY_ALL &&
R_TIME_SPEED != 0 &&
@ -418,6 +431,10 @@ void autosave_post_play_update(PlayState* play) {
gSaveContext.save.cutsceneIndex < 0xFFF0 &&
!Play_InCsMode(play) &&
!reached_final_three_hours() &&
gSaveContext.minigameStatus == MINIGAME_STATUS_END &&
gSaveContext.timerStates[TIMER_ID_MINIGAME_1] == TIMER_STATE_OFF &&
gSaveContext.timerStates[TIMER_ID_MINIGAME_2] == TIMER_STATE_OFF &&
!(CHECK_EVENTINF(EVENTINF_41)) &&
gCanPause
) {
frames_since_autosave_ready++;
@ -612,6 +629,9 @@ RECOMP_PATCH void Sram_OpenSave(FileSelectState* fileSelect, SramContext* sramCt
}
// @recomp Handle autosaves.
else if (gSaveContext.save.isOwlSave == SAVE_TYPE_AUTOSAVE) {
// Clear Rock Sirloin from being held, due to MM hardcoding its behavior
gSaveContext.unk_1014 = 0;
gSaveContext.save.entrance = spawn_entrance_from_autosave_entrance(gSaveContext.save.entrance);
// Skip the turtle cutscene that happens when entering Great Bay Temple.

View file

@ -76,6 +76,17 @@ void camera_post_play_update(PlayState* play) {
if (force_interpolation) {
force_camera_interpolation();
}
// Dedicated section for workarounds where the heuristic fails to detect small camera teleports.
bool force_no_interpolation = false;
// Music Box House. The camera gets teleported by a very small amount when Link gets the Gibdo mask.
if (play->sceneId == SCENE_MUSICHOUSE && play->csCtx.scriptIndex == 2 && play->csCtx.curFrame == 525 && active_cam->setting == CAM_SET_FREE0) {
force_no_interpolation = true;
}
if (force_no_interpolation) {
force_camera_skip_interpolation();
}
}
}
}

View file

@ -14,3 +14,33 @@ symbols = [
{ name = "FileSelect_Init_NORELOCATE", vram = 0x80813C98 },
{ name = "DayTelop_Init_NORELOCATE", vram = 0x80815820 },
]
[[section]]
name = "..ovl_En_Hg"
rom = 0x01034170
vram = 0x80BCF1D0
size = 0x10E0
symbols = [
{ name = "sPamelasFatherGibdoAnimationInfo", vram = 0x80bd0008 },
]
[[section]]
name = "..ovl_En_Hgo"
rom = 0x01035250
vram = 0x80BD02B0
size = 0xF30
symbols = [
{ name = "sPamelasFatherHumanAnimationInfo", vram = 0x80BD0EA0 },
]
[[section]]
name = "..ovl_En_Pamera"
rom = 0x0103D250
vram = 0x80BD82B0
size = 0x2780
symbols = [
{ name = "sPamelaAnimationInfo", vram = 0x80BDA4B8 },
]

View file

@ -0,0 +1,45 @@
typedef enum PamelasFatherGibdoLimb {
/* 0x00 */ PAMELAS_FATHER_GIBDO_LIMB_NONE,
/* 0x01 */ PAMELAS_FATHER_GIBDO_LIMB_ROOT,
/* 0x02 */ PAMELAS_FATHER_GIBDO_LIMB_ABDOMEN,
/* 0x03 */ PAMELAS_FATHER_GIBDO_LIMB_CHEST,
/* 0x04 */ PAMELAS_FATHER_GIBDO_LIMB_LEFT_UPPER_ARM,
/* 0x05 */ PAMELAS_FATHER_GIBDO_LIMB_LEFT_FOREARM,
/* 0x06 */ PAMELAS_FATHER_GIBDO_LIMB_LEFT_HAND,
/* 0x07 */ PAMELAS_FATHER_GIBDO_LIMB_RIGHT_UPPER_ARM,
/* 0x08 */ PAMELAS_FATHER_GIBDO_LIMB_RIGHT_FOREARM,
/* 0x09 */ PAMELAS_FATHER_GIBDO_LIMB_RIGHT_HAND,
/* 0x0A */ PAMELAS_FATHER_GIBDO_LIMB_EYEBROWS,
/* 0x0B */ PAMELAS_FATHER_GIBDO_LIMB_HEAD,
/* 0x0C */ PAMELAS_FATHER_GIBDO_LIMB_PELVIS,
/* 0x0D */ PAMELAS_FATHER_GIBDO_LIMB_LEFT_THIGH,
/* 0x0E */ PAMELAS_FATHER_GIBDO_LIMB_LEFT_SHIN,
/* 0x0F */ PAMELAS_FATHER_GIBDO_LIMB_LEFT_FOOT,
/* 0x10 */ PAMELAS_FATHER_GIBDO_LIMB_RIGHT_THIGH,
/* 0x11 */ PAMELAS_FATHER_GIBDO_LIMB_RIGHT_SHIN,
/* 0x12 */ PAMELAS_FATHER_GIBDO_LIMB_RIGHT_FOOT,
/* 0x13 */ PAMELAS_FATHER_GIBDO_LIMB_MAX
} PamelasFatherGibdoLimb;
typedef enum PamelasFatherHumanLimb {
/* 0x00 */ PAMELAS_FATHER_HUMAN_LIMB_NONE,
/* 0x01 */ PAMELAS_FATHER_HUMAN_LIMB_ROOT,
/* 0x02 */ PAMELAS_FATHER_HUMAN_LIMB_ABDOMEN,
/* 0x03 */ PAMELAS_FATHER_HUMAN_LIMB_CHEST,
/* 0x04 */ PAMELAS_FATHER_HUMAN_LIMB_LEFT_UPPER_ARM,
/* 0x05 */ PAMELAS_FATHER_HUMAN_LIMB_LEFT_FOREARM,
/* 0x06 */ PAMELAS_FATHER_HUMAN_LIMB_LEFT_HAND,
/* 0x07 */ PAMELAS_FATHER_HUMAN_LIMB_RIGHT_UPPER_ARM,
/* 0x08 */ PAMELAS_FATHER_HUMAN_LIMB_RIGHT_FOREARM,
/* 0x09 */ PAMELAS_FATHER_HUMAN_LIMB_RIGHT_HAND,
/* 0x0A */ PAMELAS_FATHER_HUMAN_LIMB_EYEBROWS,
/* 0x0B */ PAMELAS_FATHER_HUMAN_LIMB_HEAD,
/* 0x0C */ PAMELAS_FATHER_HUMAN_LIMB_PELVIS,
/* 0x0D */ PAMELAS_FATHER_HUMAN_LIMB_LEFT_THIGH,
/* 0x0E */ PAMELAS_FATHER_HUMAN_LIMB_LEFT_SHIN,
/* 0x0F */ PAMELAS_FATHER_HUMAN_LIMB_LEFT_FOOT,
/* 0x10 */ PAMELAS_FATHER_HUMAN_LIMB_RIGHT_THIGH,
/* 0x11 */ PAMELAS_FATHER_HUMAN_LIMB_RIGHT_SHIN,
/* 0x12 */ PAMELAS_FATHER_HUMAN_LIMB_RIGHT_FOOT,
/* 0x13 */ PAMELAS_FATHER_HUMAN_LIMB_MAX
} PamelasFatherHumanLimb;

View file

@ -0,0 +1,26 @@
typedef enum PamelaLimb {
/* 0x00 */ PAMELA_LIMB_NONE,
/* 0x01 */ PAMELA_LIMB_ROOT,
/* 0x02 */ PAMELA_LIMB_UPPER_BODY_ROOT,
/* 0x03 */ PAMELA_LIMB_LEFT_UPPER_ARM,
/* 0x04 */ PAMELA_LIMB_LEFT_FOREARM,
/* 0x05 */ PAMELA_LIMB_LEFT_HAND,
/* 0x06 */ PAMELA_LIMB_RIGHT_UPPER_ARM,
/* 0x07 */ PAMELA_LIMB_RIGHT_FOREARM,
/* 0x08 */ PAMELA_LIMB_RIGHT_HAND,
/* 0x09 */ PAMELA_LIMB_HEAD,
/* 0x0A */ PAMELA_LIMB_HAIR_END,
/* 0x0B */ PAMELA_LIMB_CHEST,
/* 0x0C */ PAMELA_LIMB_NECK,
/* 0x0D */ PAMELA_LIMB_LEFT_THIGH,
/* 0x0E */ PAMELA_LIMB_LEFT_LEG,
/* 0x0F */ PAMELA_LIMB_LEFT_FOOT,
/* 0x10 */ PAMELA_LIMB_RIGHT_THIGH,
/* 0x11 */ PAMELA_LIMB_RIGHT_LEG,
/* 0x12 */ PAMELA_LIMB_RIGHT_FOOT,
/* 0x13 */ PAMELA_LIMB_FRONT_DRESS,
/* 0x14 */ PAMELA_LIMB_BACK_DRESS,
/* 0x15 */ PAMELA_LIMB_ABDOMEN,
/* 0x16 */ PAMELA_LIMB_PELVIS,
/* 0x17 */ PAMELA_LIMB_MAX
} PamelaLimb;

View file

@ -0,0 +1,107 @@
#include "patches.h"
#include "transform_ids.h"
#include "overlays/actors/ovl_En_Hgo/z_en_hgo.h"
typedef enum {
/* 0 */ HGO_ANIM_ARMS_FOLDED,
/* 1 */ HGO_ANIM_ASTONISHED,
/* 2 */ HGO_ANIM_KNEEL_DOWN_AND_HUG,
/* 3 */ HGO_ANIM_CONSOLE,
/* 4 */ HGO_ANIM_CONSOLE_HEAD_UP,
/* 5 */ HGO_ANIM_REACH_DOWN_TO_LIFT,
/* 6 */ HGO_ANIM_TOSS,
/* 7 */ HGO_ANIM_MAX
} HgoAnimation;
extern AnimationInfo sPamelasFatherHumanAnimationInfo[];
extern void EnHgo_Draw(Actor* thisx, PlayState* play);
extern void EnHgo_DoNothing(EnHgo* this, PlayState* play);
extern void EnHgo_SetupInitCollision(EnHgo* this);
// @recomp Skip interpolation when the animations change during the cutscene, as the
// animation changes are meant to happen at the same time as the camera cuts.
RECOMP_PATCH s32 EnHgo_HandleCsAction(EnHgo* this, PlayState* play) {
s32 cueChannel;
if (Cutscene_IsCueInChannel(play, CS_CMD_ACTOR_CUE_486)) {
cueChannel = Cutscene_GetCueChannel(play, CS_CMD_ACTOR_CUE_486);
if (this->cueId != play->csCtx.actorCues[cueChannel]->id) {
this->cueId = play->csCtx.actorCues[cueChannel]->id;
switch (play->csCtx.actorCues[cueChannel]->id) {
case 1:
this->animIndex = HGO_ANIM_ARMS_FOLDED;
Actor_ChangeAnimationByInfo(&this->skelAnime, sPamelasFatherHumanAnimationInfo, HGO_ANIM_ARMS_FOLDED);
break;
case 2:
this->actor.draw = EnHgo_Draw;
this->animIndex = HGO_ANIM_ASTONISHED;
Actor_ChangeAnimationByInfo(&this->skelAnime, sPamelasFatherHumanAnimationInfo, HGO_ANIM_ASTONISHED);
break;
case 3:
this->animIndex = HGO_ANIM_KNEEL_DOWN_AND_HUG;
Actor_ChangeAnimationByInfo(&this->skelAnime, sPamelasFatherHumanAnimationInfo, HGO_ANIM_KNEEL_DOWN_AND_HUG);
break;
case 4:
this->animIndex = HGO_ANIM_CONSOLE;
Actor_ChangeAnimationByInfo(&this->skelAnime, sPamelasFatherHumanAnimationInfo, HGO_ANIM_CONSOLE);
break;
case 5:
this->animIndex = HGO_ANIM_CONSOLE_HEAD_UP;
Actor_ChangeAnimationByInfo(&this->skelAnime, sPamelasFatherHumanAnimationInfo, HGO_ANIM_CONSOLE_HEAD_UP);
break;
case 6:
this->animIndex = HGO_ANIM_REACH_DOWN_TO_LIFT;
Actor_ChangeAnimationByInfo(&this->skelAnime, sPamelasFatherHumanAnimationInfo, HGO_ANIM_REACH_DOWN_TO_LIFT);
break;
default:
break;
}
actor_set_interpolation_skipped(&this->actor);
} else if (Animation_OnFrame(&this->skelAnime, this->skelAnime.endFrame)) {
switch (this->animIndex) {
case HGO_ANIM_ASTONISHED:
if (Animation_OnFrame(&this->skelAnime, this->skelAnime.endFrame) && !this->isInCutscene) {
this->isInCutscene = true;
if ((gSaveContext.sceneLayer == 0) &&
((play->csCtx.scriptIndex == 2) || (play->csCtx.scriptIndex == 4))) {
Actor_PlaySfx(&this->actor, NA_SE_VO_GBVO02);
}
}
break;
case HGO_ANIM_KNEEL_DOWN_AND_HUG:
this->animIndex = HGO_ANIM_CONSOLE;
Actor_ChangeAnimationByInfo(&this->skelAnime, sPamelasFatherHumanAnimationInfo, HGO_ANIM_CONSOLE);
break;
case HGO_ANIM_REACH_DOWN_TO_LIFT:
this->animIndex = HGO_ANIM_TOSS;
Actor_ChangeAnimationByInfo(&this->skelAnime, sPamelasFatherHumanAnimationInfo, HGO_ANIM_TOSS);
default:
break;
}
}
Cutscene_ActorTranslateAndYaw(&this->actor, play, cueChannel);
return true;
}
if ((play->csCtx.state == CS_STATE_IDLE) && CHECK_WEEKEVENTREG(WEEKEVENTREG_75_20) &&
(this->actionFunc == EnHgo_DoNothing)) {
this->actor.shape.rot.y = this->actor.world.rot.y;
Actor_Spawn(&play->actorCtx, play, ACTOR_ELF_MSG2, this->actor.focus.pos.x, this->actor.focus.pos.y,
this->actor.focus.pos.z, 7, 0, 0, 0x7F5A);
EnHgo_SetupInitCollision(this);
}
this->cueId = 99;
return false;
}

View file

@ -0,0 +1,26 @@
#include "patches.h"
#include "transform_ids.h"
#include "overlays/actors/ovl_Dm_Char05/z_dm_char05.h"
extern void func_80AADF54(PlayState* play, DmChar05* this);
// @recomp Patched to avoid an interpolation glitch in Pamela's dad's cutscene
// that happens when the mask is meant to teleport offscreen.
RECOMP_PATCH void func_80AADB4C(Actor* thisx, PlayState* play) {
DmChar05* this = (DmChar05*)thisx;
if (this->unk_18E == 0) {
if (Cutscene_IsCueInChannel(play, CS_CMD_ACTOR_CUE_518) &&
(play->csCtx.actorCues[Cutscene_GetCueChannel(play, CS_CMD_ACTOR_CUE_518)]->id != 1)) {
// @recomp During this cue the mask does nothing other than teleport offscreen and stay still,
// so we can just skip interpolation the entire time.
if (play->csCtx.actorCues[Cutscene_GetCueChannel(play, CS_CMD_ACTOR_CUE_518)]->id == 3) {
actor_set_interpolation_skipped(thisx);
}
Gfx_SetupDL25_Opa(play->state.gfxCtx);
SkelAnime_DrawFlexOpa(play, this->skelAnime.skeleton, this->skelAnime.jointTable,
this->skelAnime.dListCount, NULL, NULL, &this->actor);
}
} else if (this->unk_18E == 1) {
func_80AADF54(play, this);
}
}

View file

@ -0,0 +1,74 @@
#include "patches.h"
#include "transform_ids.h"
#include "overlays/actors/ovl_En_Pamera/z_en_pamera.h"
extern void EnPamera_Draw(Actor* thisx, PlayState* play);
extern void func_80BD9E88(EnPamera* this);
extern void func_80BD9EE0(EnPamera* this);
extern void func_80BDA038(EnPamera* this);
extern void func_80BDA0A0(EnPamera* this);
extern void func_80BDA170(EnPamera* this);
extern void func_80BDA288(EnPamera* this);
extern void func_80BD994C(EnPamera* this, PlayState* play);
extern void EnPamera_HandleDialogue(EnPamera* this, PlayState* play);
extern void func_80BD9904(EnPamera* this);
extern void func_80BD9E60(EnPamera* this);
// @recomp Skip interpolation when the animations change during the cutscene, as the
// animation changes are meant to happen at the same time as the camera cuts.
RECOMP_PATCH s32 func_80BD9CB8(EnPamera* this, PlayState* play) {
s32 cueChannel;
if (Cutscene_IsCueInChannel(play, CS_CMD_ACTOR_CUE_485)) {
cueChannel = Cutscene_GetCueChannel(play, CS_CMD_ACTOR_CUE_485);
if (this->cueId != play->csCtx.actorCues[cueChannel]->id) {
this->cueId = play->csCtx.actorCues[cueChannel]->id;
switch (play->csCtx.actorCues[cueChannel]->id) {
case 1:
func_80BD9E88(this);
break;
case 2:
if (this->actor.draw == NULL) {
this->actor.draw = EnPamera_Draw;
this->actor.flags |= ACTOR_FLAG_TARGETABLE;
}
func_80BD9EE0(this);
break;
case 3:
func_80BDA038(this);
break;
case 4:
func_80BDA0A0(this);
break;
case 5:
func_80BDA170(this);
break;
case 6:
func_80BDA288(this);
break;
default:
break;
}
actor_set_interpolation_skipped(&this->actor);
}
Cutscene_ActorTranslateAndYaw(&this->actor, play, cueChannel);
this->setupFunc(this, play);
return true;
}
if ((play->csCtx.state == CS_STATE_IDLE) && CHECK_WEEKEVENTREG(WEEKEVENTREG_75_20)) {
if ((this->actionFunc != func_80BD994C) && (this->actionFunc != EnPamera_HandleDialogue)) {
this->actor.shape.rot.y = this->actor.world.rot.y;
func_80BD9904(this);
func_80BD9E60(this);
}
}
this->cueId = 99;
return false;
}

View file

@ -185,3 +185,6 @@ RECOMP_PATCH void Player_DrawGameplay(PlayState* play, Player* this, s32 lod, Gf
CLOSE_DISPS(play->state.gfxCtx);
}
RECOMP_EXPORT u32 z64recomp_get_bowstring_transform_id() {
return BOWSTRING_TRANSFORM_ID;
}

View file

@ -12,6 +12,8 @@
#include "overlays/actors/ovl_En_Twig/z_en_twig.h"
#include "overlays/actors/ovl_En_Honotrap/z_en_honotrap.h"
#include "overlays/actors/ovl_En_Tanron1/z_en_tanron1.h"
#include "overlays/actors/ovl_En_Kusa2/z_en_kusa2.h"
#include "overlays/actors/ovl_Obj_Grass/z_obj_grass.h"
// Decomp renames, TODO update decomp and remove these
#define EnHonotrap_FlameGroup func_8092F878
@ -1331,3 +1333,154 @@ RECOMP_PATCH void func_80BB5AAC(EnTanron1* this, PlayState* play) {
CLOSE_DISPS(play->state.gfxCtx);
}
extern Gfx gKakeraLeafTipDL[];
extern Gfx gKakeraLeafMiddleDL[];
extern EnKusa2UnkBssStruct D_80A5F1C0;
// Patched to tag the particles that spawn from Keaton grass.
RECOMP_PATCH void func_80A5E6F0(Actor* thisx, PlayState* play) {
static Gfx* D_80A5EB68[] = {
gKakeraLeafTipDL,
gKakeraLeafMiddleDL,
};
EnKusa2* this = (EnKusa2*)thisx;
s32 i;
OPEN_DISPS(play->state.gfxCtx);
Gfx_SetupDL25_Opa(play->state.gfxCtx);
// @recomp Get the base transform ID for this actor.
u32 cur_transform_id = actor_transform_id(thisx);
for (i = 0; i < ARRAY_COUNT(D_80A5F1C0.unk_0480); i++) {
EnKusa2UnkBssSubStruct2* s = &D_80A5F1C0.unk_0480[i];
if (s->unk_2C > 0) {
Matrix_SetTranslateRotateYXZ(s->unk_04.x, s->unk_04.y, s->unk_04.z, &s->unk_20);
Matrix_Scale(s->unk_00, s->unk_00, s->unk_00, MTXMODE_APPLY);
// @recomp Create a matrix group for this particle.
gEXMatrixGroupDecomposedNormal(POLY_OPA_DISP++, cur_transform_id + i, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_ALLOW);
gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_OPA_DISP++, D_80A5EB68[i & 1]);
// @recomp Pop the matrix group.
gEXPopMatrixGroup(POLY_OPA_DISP++, G_MTX_MODELVIEW);
}
}
CLOSE_DISPS(play->state.gfxCtx);
}
extern Gfx gObjGrass_D_809AA9F0[];
extern Gfx gObjGrass_D_809AAA68[];
extern Gfx gObjGrass_D_809AAAE0[];
void ObjGrass_OverrideMatrixCurrent(MtxF* matrix);
// @recomp Patched to set matrix groups for grass.
RECOMP_PATCH void ObjGrass_DrawOpa(Actor* thisx, PlayState* play2) {
ObjGrass* this = (ObjGrass*)thisx;
PlayState* play = play2;
Lights* lights;
ObjGrassGroup* grassGroup;
s32 i;
s32 j;
Vec3s rot = { 0, 0, 0 };
ObjGrassElement* grassElem;
OPEN_DISPS(play->state.gfxCtx);
Gfx_SetupDL25_Opa(play->state.gfxCtx);
gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 255, 255, 255, 255);
gSPDisplayList(POLY_OPA_DISP++, gObjGrass_D_809AA9F0);
// @recomp Extract this actor's ID.
u32 actor_id = actor_transform_id(thisx);
for (i = 0; i < this->activeGrassGroups; i++) {
grassGroup = &this->grassGroups[i];
if (grassGroup->flags & OBJ_GRASS_GROUP_DRAW) {
lights = LightContext_NewLights(&play->lightCtx, play->state.gfxCtx);
Lights_BindAll(lights, play->lightCtx.listHead, &grassGroup->homePos, play);
Lights_Draw(lights, play->state.gfxCtx);
for (j = 0; j < grassGroup->count; j++) {
grassElem = &grassGroup->elements[j];
if ((grassElem->flags & OBJ_GRASS_ELEM_DRAW) && (grassElem->alpha == 255)) {
rot.y = grassElem->rotY;
Matrix_SetTranslateRotateYXZ(grassElem->pos.x, grassElem->pos.y, grassElem->pos.z, &rot);
Matrix_Scale(this->actor.scale.x, this->actor.scale.y, this->actor.scale.z, MTXMODE_APPLY);
if (grassElem->flags & OBJ_GRASS_ELEM_ANIM) {
ObjGrass_OverrideMatrixCurrent(&this->distortionMtx[j]);
}
// @recomp Push a matrix group.
gEXMatrixGroupDecomposedNormal(POLY_OPA_DISP++, actor_id + i * OBJ_GRASS_GROUP_ELEM_COUNT_MAX + j, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_NONE);
gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(play->state.gfxCtx),
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_OPA_DISP++, gObjGrass_D_809AAAE0);
// @recomp Pop the matrix group.
gEXPopMatrixGroup(POLY_OPA_DISP++, G_MTX_MODELVIEW);
}
}
}
}
CLOSE_DISPS(play->state.gfxCtx);
}
// @recomp Patched to set matrix groups for grass.
RECOMP_PATCH void ObjGrass_DrawXlu(Actor* thisx, PlayState* play) {
ObjGrass* this = (ObjGrass*)thisx;
ObjGrassGroup* grassGroup;
ObjGrassElement* grassElem;
s32 i;
s32 j;
Vec3s rot = { 0, 0, 0 };
OPEN_DISPS(play->state.gfxCtx);
Gfx_SetupDL25_Xlu(play->state.gfxCtx);
gSPDisplayList(POLY_XLU_DISP++, gObjGrass_D_809AAA68);
// @recomp Extract this actor's ID.
u32 actor_id = actor_transform_id(thisx);
for (i = 0; i < this->activeGrassGroups; i++) {
grassGroup = &this->grassGroups[i];
if (grassGroup->flags & OBJ_GRASS_GROUP_DRAW) {
for (j = 0; j < grassGroup->count; j++) {
grassElem = &grassGroup->elements[j];
if ((grassElem->flags & OBJ_GRASS_ELEM_DRAW) && (grassElem->alpha > 0) && (grassElem->alpha < 255)) {
rot.y = grassElem->rotY;
Matrix_SetTranslateRotateYXZ(grassElem->pos.x, grassElem->pos.y, grassElem->pos.z, &rot);
Matrix_Scale(this->actor.scale.x, this->actor.scale.y, this->actor.scale.z, MTXMODE_APPLY);
// @recomp Push a matrix group.
gEXMatrixGroupDecomposedNormal(POLY_XLU_DISP++, actor_id + i * OBJ_GRASS_GROUP_ELEM_COUNT_MAX + j, G_EX_PUSH, G_MTX_MODELVIEW, G_EX_EDIT_NONE);
gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(play->state.gfxCtx),
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 255, grassElem->alpha);
gSPDisplayList(POLY_XLU_DISP++, gObjGrass_D_809AAAE0);
// @recomp Pop the matrix group.
gEXPopMatrixGroup(POLY_XLU_DISP++, G_MTX_MODELVIEW);
}
}
}
}
CLOSE_DISPS(play->state.gfxCtx);
}

View file

@ -0,0 +1,509 @@
#include "patches.h"
#define VTX_EX_T(x, y, z, s, t, cr, cg, cb, a, px, py, pz) \
{ { x, y, z }, 0, { s, t }, { cr, cg, cb, a }, {px, py, pz} }
void EffectBlure_GetComputedValues(EffectBlure* this, s32 index, f32 ratio, Vec3s* vec1, Vec3s* vec2,
Color_RGBA8* color1, Color_RGBA8* color2);
void EffectBlure_DrawSimple(EffectBlure* this2, GraphicsContext* gfxCtx);
void EffectBlure_DrawSmooth(EffectBlure* this2, GraphicsContext* gfxCtx);
// @recomp Patched to interpolate the vertices towards the front of the trail section if this is the last trail being drawn currently.
RECOMP_PATCH void EffectBlure_DrawElemNoInterpolation(EffectBlure* this, EffectBlureElement* elem, s32 index,
GraphicsContext* gfxCtx) {
// @recomp Change baseVtx to a VertexEX.
static VertexEXColor baseVtx = VTX_EX_T(/* pos */ 0, 0, 0, /* st */ 0, 0, /* color */ 255, 255, 255, 255, /* prev pos */ 0, 0, 0);
// @recomp Change the vertex type to VertexEX.
VertexEX* vtx;
Vec3s sp8C;
Vec3s sp84;
f32 ratio;
Color_RGBA8 sp7C;
Color_RGBA8 sp78;
Vec3f sp6C;
Vec3f sp60;
Vec3f sp54;
OPEN_DISPS(gfxCtx);
Math_Vec3s_ToVec3f(&sp6C, &this->elements[0].p2);
// @recomp Debug print.
// recomp_printf("No interpolation index %d:\n"
// " Blure: calcMode %08X flags: %04X addAngle %04X addAngle %04X elemDuration %d\n"
// " Element: state %08X timer: %d flags %04X\n",
// index,
// this->calcMode, this->flags, (u16)this->addAngleChange, (u16)this->addAngle, this->elemDuration,
// elem->state, elem->timer, elem->flags);
// @recomp Allocate using the size of VertexEX instead.
vtx = GRAPH_ALLOC(gfxCtx, 4 * sizeof(VertexEX));
if (vtx == NULL) {
} 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;
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;
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;
// @recomp Set the previous position of the first two vertices to their current position.
vtx[0].v.obp[0] = vtx[0].v.ob[0];
vtx[0].v.obp[1] = vtx[0].v.ob[1];
vtx[0].v.obp[2] = vtx[0].v.ob[2];
vtx[1].v.obp[0] = vtx[1].v.ob[0];
vtx[1].v.obp[1] = vtx[1].v.ob[1];
vtx[1].v.obp[2] = vtx[1].v.ob[2];
// @recomp If this trail just spawned (timer == 2), set the previous vertex positions for the last two vertices to the positions of the last two (interpolation).
// Otherwise, set them to the current position of the respective vertex (no interpolation).
if (elem->timer == 2) {
// Vertex 2 interpolates from a start position equal to the position of vertex 1.
vtx[2].v.obp[0] = vtx[1].v.ob[0];
vtx[2].v.obp[1] = vtx[1].v.ob[1];
vtx[2].v.obp[2] = vtx[1].v.ob[2];
// Vertex 3 interpolates from a start position equal to the position of vertex 0.
vtx[3].v.obp[0] = vtx[0].v.ob[0];
vtx[3].v.obp[1] = vtx[0].v.ob[1];
vtx[3].v.obp[2] = vtx[0].v.ob[2];
}
else {
vtx[2].v.obp[0] = vtx[2].v.ob[0];
vtx[2].v.obp[1] = vtx[2].v.ob[1];
vtx[2].v.obp[2] = vtx[2].v.ob[2];
vtx[3].v.obp[0] = vtx[3].v.ob[0];
vtx[3].v.obp[1] = vtx[3].v.ob[1];
vtx[3].v.obp[2] = vtx[3].v.ob[2];
}
// @recomp Use gEXVertex in place of gSPVertex.
gEXVertex(POLY_XLU_DISP++, vtx, 4, 0);
gSP2Triangles(POLY_XLU_DISP++, 0, 1, 2, 0, 0, 2, 3, 0);
}
CLOSE_DISPS(gfxCtx);
}
// @recomp Patched to interpolate the vertices towards the front of the trail section if this is the last trail being drawn currently.
RECOMP_PATCH void EffectBlure_DrawElemHermiteInterpolation(EffectBlure* this, EffectBlureElement* elem, s32 index,
GraphicsContext* gfxCtx) {
// @recomp Change baseVtx to a VertexEX.
static VertexEXColor baseVtx = VTX_EX_T(/* pos */ 0, 0, 0, /* st */ 0, 0, /* color */ 255, 255, 255, 255, /* prev pos */ 0, 0, 0);
// @recomp Change the vertex type to VertexEX.
VertexEX* 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;
// @recomp Debug print.
// recomp_printf("Hermite interpolation index %d:\n"
// " Blure: calcMode %08X flags: %04X addAngle %04X addAngle %04X elemDuration %d\n"
// " Element: state %08X timer: %d flags %04X\n",
// index,
// this->calcMode, this->flags, (u16)this->addAngleChange, (u16)this->addAngle, this->elemDuration,
// elem->state, elem->timer, elem->flags);
OPEN_DISPS(gfxCtx);
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 & (EFFECT_BLURE_ELEMENT_FLAG_1 | EFFECT_BLURE_ELEMENT_FLAG_2)) == EFFECT_BLURE_ELEMENT_FLAG_2) {
Math_Vec3f_Diff(&sp18C, &sp1CC, &sp1B4);
Math_Vec3f_Diff(&sp180, &sp1C0, &sp1A8);
} else {
Vec3f sp118;
Vec3f sp10C;
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 & (EFFECT_BLURE_ELEMENT_FLAG_1 | EFFECT_BLURE_ELEMENT_FLAG_2)) ==
EFFECT_BLURE_ELEMENT_FLAG_2) {
Math_Vec3f_Diff(&sp18C, &sp1CC, &sp174);
Math_Vec3f_Diff(&sp180, &sp1C0, &sp168);
} else {
Vec3f sp100;
Vec3f spF4;
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);
// @recomp Allocate using the size of VertexEX instead.
vtx = GRAPH_ALLOC(gfxCtx, 16 * sizeof(VertexEX));
if (vtx == NULL) {
} 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;
// @recomp Set the previous position of the first two vertices to their current position.
vtx[0].v.obp[0] = vtx[0].v.ob[0];
vtx[0].v.obp[1] = vtx[0].v.ob[1];
vtx[0].v.obp[2] = vtx[0].v.ob[2];
vtx[1].v.obp[0] = vtx[1].v.ob[0];
vtx[1].v.obp[1] = vtx[1].v.ob[1];
vtx[1].v.obp[2] = vtx[1].v.ob[2];
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 = SQ(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_800B0A24(sp1A4.r, sp19C.r, temp_f28);
vtx[j1].v.cn[1] = func_800B0A24(sp1A4.g, sp19C.g, temp_f28);
vtx[j1].v.cn[2] = func_800B0A24(sp1A4.b, sp19C.b, temp_f28);
vtx[j1].v.cn[3] = func_800B0A24(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_800B0A24(sp1A0.r, sp198.r, temp_f28);
vtx[j2].v.cn[1] = func_800B0A24(sp1A0.g, sp198.g, temp_f28);
vtx[j2].v.cn[2] = func_800B0A24(sp1A0.b, sp198.b, temp_f28);
vtx[j2].v.cn[3] = func_800B0A24(sp1A0.a, sp198.a, temp_f28);
// @recomp If this trail just spawned (timer == 2), set the previous vertex positions for the remaining vertices to the positions of the first two (interpolation).
// Otherwise, set them to the current position of the respective vertex (no interpolation).
if (elem->timer == 2) {
vtx[j1].v.obp[0] = vtx[0].v.ob[0];
vtx[j1].v.obp[1] = vtx[0].v.ob[1];
vtx[j1].v.obp[2] = vtx[0].v.ob[2];
vtx[j2].v.obp[0] = vtx[1].v.ob[0];
vtx[j2].v.obp[1] = vtx[1].v.ob[1];
vtx[j2].v.obp[2] = vtx[1].v.ob[2];
}
else {
vtx[j1].v.obp[0] = vtx[j1].v.ob[0];
vtx[j1].v.obp[1] = vtx[j1].v.ob[1];
vtx[j1].v.obp[2] = vtx[j1].v.ob[2];
vtx[j2].v.obp[0] = vtx[j2].v.ob[0];
vtx[j2].v.obp[1] = vtx[j2].v.ob[1];
vtx[j2].v.obp[2] = vtx[j2].v.ob[2];
}
}
// @recomp Use gEXVertex in place of gSPVertex.
gEXVertex(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);
}
// @recomp Patched to interpolate the vertices towards the front of the trail section if this is the last trail being drawn currently.
RECOMP_PATCH void EffectBlure_Draw(void* thisx, GraphicsContext* gfxCtx) {
EffectBlure* this = (EffectBlure*)thisx;
// @recomp Change the vertex type to VertexEX.
VertexEX* vtx;
EffectBlureElement* elem;
s32 i;
s32 j;
s32 phi_t2;
OPEN_DISPS(gfxCtx);
gSPMatrix(POLY_XLU_DISP++, &gIdentityMtx, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
if (this->numElements != 0) {
if (this->flags == 0) {
Gfx_SetupDL38_Xlu(gfxCtx);
gDPPipeSync(POLY_XLU_DISP++);
// @recomp Allocate using the size of VertexEX instead.
vtx = GRAPH_ALLOC(gfxCtx, 32 * sizeof(VertexEX));
if (vtx == NULL) {
} else {
j = 0;
for (i = 0; i < this->numElements; i++) {
elem = &this->elements[i];
// @recomp Debug print.
// recomp_printf("Flag 0 %d:\n"
// " Blure: calcMode %08X flags: %04X addAngle %04X addAngle %04X elemDuration %d\n"
// " Element: state %08X timer: %d flags %04X\n",
// i,
// this->calcMode, this->flags, (u16)this->addAngleChange, (u16)this->addAngle, this->elemDuration,
// elem->state, elem->timer, elem->flags);
if (elem->state == 1) {
f32 ratio = (f32)elem->timer / (f32)this->elemDuration;
switch (this->calcMode) {
case 1:
vtx[j].v.ob[0] = func_800B09D0(elem->p1.x, elem->p2.x, ratio);
vtx[j].v.ob[1] = func_800B09D0(elem->p1.y, elem->p2.y, ratio);
vtx[j].v.ob[2] = func_800B09D0(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_800B09D0(elem->p2.x, elem->p1.x, ratio);
vtx[j + 1].v.ob[1] = func_800B09D0(elem->p2.y, elem->p1.y, ratio);
vtx[j + 1].v.ob[2] = func_800B09D0(elem->p2.z, elem->p1.z, ratio);
break;
case 3:
ratio *= 0.5f;
vtx[j].v.ob[0] = func_800B09D0(elem->p1.x, elem->p2.x, ratio);
vtx[j].v.ob[1] = func_800B09D0(elem->p1.y, elem->p2.y, ratio);
vtx[j].v.ob[2] = func_800B09D0(elem->p1.z, elem->p2.z, ratio);
vtx[j + 1].v.ob[0] = func_800B09D0(elem->p2.x, elem->p1.x, ratio);
vtx[j + 1].v.ob[1] = func_800B09D0(elem->p2.y, elem->p1.y, ratio);
vtx[j + 1].v.ob[2] = func_800B09D0(elem->p2.z, elem->p1.z, ratio);
ratio *= 2.0f;
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;
}
// @recomp If this trail just spawned (timer == 1), set the previous vertex positions for this vertex that of the second previous vertex (interpolation).
// Otherwise, set them to the current position of the respective vertex (no interpolation).
if (elem->timer == 1 && j >= 2) {
vtx[j].v.obp[0] = vtx[j - 2].v.ob[0];
vtx[j].v.obp[1] = vtx[j - 2].v.ob[1];
vtx[j].v.obp[2] = vtx[j - 2].v.ob[2];
}
else {
vtx[j].v.obp[0] = vtx[j].v.ob[0];
vtx[j].v.obp[1] = vtx[j].v.ob[1];
vtx[j].v.obp[2] = vtx[j].v.ob[2];
}
vtx[j].v.flag = 0;
vtx[j].v.tc[0] = 0;
vtx[j].v.tc[1] = 0;
vtx[j].v.cn[0] = func_800B0A24(this->p1StartColor[0], this->p1EndColor[0], ratio);
vtx[j].v.cn[1] = func_800B0A24(this->p1StartColor[1], this->p1EndColor[1], ratio);
vtx[j].v.cn[2] = func_800B0A24(this->p1StartColor[2], this->p1EndColor[2], ratio);
vtx[j].v.cn[3] = func_800B0A24(this->p1StartColor[3], this->p1EndColor[3], ratio);
j++;
// @recomp If this trail just spawned (timer == 1), set the previous vertex positions for this vertex that of the second previous vertex (interpolation).
// Otherwise, set them to the current position of the respective vertex (no interpolation).
if (elem->timer == 1 && j >= 2) {
vtx[j].v.obp[0] = vtx[j - 2].v.ob[0];
vtx[j].v.obp[1] = vtx[j - 2].v.ob[1];
vtx[j].v.obp[2] = vtx[j - 2].v.ob[2];
}
else {
vtx[j].v.obp[0] = vtx[j].v.ob[0];
vtx[j].v.obp[1] = vtx[j].v.ob[1];
vtx[j].v.obp[2] = vtx[j].v.ob[2];
}
vtx[j].v.flag = 0;
vtx[j].v.tc[0] = 0;
vtx[j].v.tc[1] = 0;
vtx[j].v.cn[0] = func_800B0A24(this->p2StartColor[0], this->p2EndColor[0], ratio);
vtx[j].v.cn[1] = func_800B0A24(this->p2StartColor[1], this->p2EndColor[1], ratio);
vtx[j].v.cn[2] = func_800B0A24(this->p2StartColor[2], this->p2EndColor[2], ratio);
vtx[j].v.cn[3] = func_800B0A24(this->p2StartColor[3], this->p2EndColor[3], ratio);
j++;
}
}
phi_t2 = 0;
j = 0;
// @recomp Use gEXVertex in place of gSPVertex.
gEXVertex(POLY_XLU_DISP++, vtx, 32, 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 (this->unkFlag == 1) {
phi_t2 = 0;
}
}
j += 2;
}
}
}
} else if (this->drawMode <= EFF_BLURE_DRAW_MODE_SIMPLE_ALT_COLORS) {
EffectBlure_DrawSimple(this, gfxCtx);
} else {
EffectBlure_DrawSmooth(this, gfxCtx);
}
}
CLOSE_DISPS(gfxCtx);
}

View file

@ -17,8 +17,12 @@
03000000790000004e95000000000000,Hyperkin N64 Controller Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b7,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a5,righty:a2,start:b9,platform:Windows,
03000000790000004418000000000000,Mayflash GameCube Controller,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Windows,
03000000790000004318000000000000,Mayflash GameCube Controller Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Windows,
03000000242f00007300000000000000,Mayflash Magic NS,a:b1,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b0,y:b3,platform:Windows,
0300000079000000d218000000000000,Mayflash Magic NS,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,
03000000d620000010a7000000000000,Mayflash Magic NS,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
03000000242f0000f400000000000000,Mayflash N64 Controller Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a2,righty:a5,start:b9,platform:Windows,
03000000790000007918000000000000,Mayflash N64 Controller Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b9,leftx:a0,lefty:a1,righttrigger:b7,rightx:a3,righty:a2,start:b8,platform:Windows,
030000008f0e00001330000000000000,Mayflash Controller Adapter,a:b1,b:b2,back:b8,dpdown:h0.8,dpleft:h0.2,dpright:h0.1,dpup:h0.4,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a3~,righty:a2,start:b9,x:b0,y:b3,platform:Windows,
03000000f70600000100000000000000,N64 Adaptoid,+rightx:b2,+righty:b1,-rightx:b4,-righty:b5,a:b0,b:b3,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b6,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b7,start:b8,platform:Windows,
030000006f0e00001311000000000000,N64 Controller,+rightx:b10,+righty:b3,-rightx:b0,-righty:b11,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,platform:Windows,
03000000790000004518000000000000,NEXILUX GameCube Controller Adapter,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b2,y:b3,platform:Windows,
@ -30,8 +34,10 @@
030000009b2800006000000000000000,Raphnet GC and N64 Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:+a5,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:+a2,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Windows,
030000009b2800006100000000000000,Raphnet N64 Adapter,+rightx:b9,+righty:b7,-rightx:b8,-righty:b6,a:b0,b:b1,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b4,lefttrigger:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b3,platform:Windows,
030000009b2800006300000000000000,Raphnet N64 Adapter,+rightx:b9,+righty:b7,-rightx:b8,-righty:b6,a:b0,b:b1,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b4,lefttrigger:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b3,platform:Windows,
030000009b2800006400000000000000,Raphnet N64 Adapter,+rightx:b9,+righty:b7,-rightx:b8,-righty:b6,a:b0,b:b1,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b4,lefttrigger:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b3,platform:Windows,
03000000341200000400000000000000,RetroUSB N64 RetroPort,+rightx:b8,+righty:b10,-rightx:b9,-righty:b11,a:b7,b:b6,dpdown:b2,dpleft:b1,dpright:b0,dpup:b3,leftshoulder:b13,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b12,start:b4,platform:Windows,
03000000c82d00006928000000010000,8BitDo N64,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,platform:Mac OS X,
03000000c82d00001930000000000000,8bitdo 64 BT,platform:Windows,a:b0,b:b1,back:b17,guide:b10,start:b11,leftstick:b13,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:b8,righttrigger:b9,
03000000260900008888000088020000,Cyber Gadget GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a5,rightx:a2,righty:a3~,start:b7,x:b2,y:b3,platform:Mac OS X,
03000000790000004618000000010000,GameCube Controller Adapter,a:b4,b:b0,dpdown:b56,dpleft:b60,dpright:b52,dpup:b48,lefttrigger:a12,leftx:a0,lefty:a4,rightshoulder:b28,righttrigger:a16,rightx:a20,righty:a8,start:b36,x:b8,y:b12,platform:Mac OS X,
03000000242e0000ff0b000000010000,Hyperkin N64 Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a2,righty:a3,start:b9,platform:Mac OS X,

View file

@ -79,7 +79,7 @@ extern "C" void recomp_register_actor_extension(uint8_t* rdram, recomp_context*
}
if (actor_data_sizes.size() <= actor_type) {
actor_data_sizes.resize(2 * actor_type);
actor_data_sizes.resize(actor_type + 1);
}
// Increase the actor type's extension data size by the provided size (rounded up to a multiple of 16).

View file

@ -569,8 +569,18 @@ void recomputil_u32_slotmap_size(uint8_t* rdram, recomp_context* ctx) {
// memory slotmap.
void recomputil_create_memory_slotmap(uint8_t* rdram, recomp_context* ctx) {
(void)rdram;
_return(ctx, memory_slotmaps.create());
uint32_t element_size = _arg<0, uint32_t>(rdram, ctx);
// Create the map.
uint32_t map_key = memory_slotmaps.create();
// Retrieve the map and set its element size to the provided value.
MemorySlotmap* map;
memory_slotmaps.get(map_key, &map);
map->second = element_size;
// Return the created map's key.
_return(ctx, map_key);
}
void recomputil_destroy_memory_slotmap(uint8_t* rdram, recomp_context* ctx) {
@ -628,7 +638,7 @@ void recomputil_memory_slotmap_create(uint8_t* rdram, recomp_context* ctx) {
// Store the allocated pointer.
PTR(void)* value_ptr;
map->first.get(key, &value_ptr);
MEM_W(0, *value_ptr) = addr;
*value_ptr = static_cast<PTR(void)>(addr);
// Return the key.
_return(ctx, key);

View file

@ -18,6 +18,13 @@
#else
#include "SDL2/SDL.h"
#include "SDL2/SDL_syswm.h"
// Undefine x11 macros that get included by SDL_syswm.h.
#undef None
#undef Status
#undef LockMask
#undef ControlMask
#undef Success
#undef Always
#endif
#include "recomp_ui.h"
@ -43,12 +50,13 @@
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <timeapi.h>
#include "SDL_syswm.h"
#endif
#include "../../lib/rt64/src/contrib/stb/stb_image.h"
const std::string version_string = "1.2.0";
const std::string version_string = "1.2.2";
template<typename... Ts>
void exit_error(const char* str, Ts ...args) {
@ -579,6 +587,26 @@ int main(int argc, char** argv) {
}
#ifdef _WIN32
// Set up high resolution timing period.
timeBeginPeriod(1);
// Process arguments.
for (int i = 1; i < argc; i++)
{
if (strcmp(argv[i], "--show-console") == 0)
{
if (GetConsoleWindow() == nullptr)
{
AllocConsole();
freopen("CONIN$", "r", stdin);
freopen("CONOUT$", "w", stderr);
freopen("CONOUT$", "w", stdout);
}
break;
}
}
// Set up console output to accept UTF-8 on windows
SetConsoleOutputCP(CP_UTF8);
@ -722,5 +750,10 @@ int main(int argc, char** argv) {
release_preload(preload_context);
}
#ifdef _WIN32
// End high resolution timing period.
timeEndPeriod(1);
#endif
return EXIT_SUCCESS;
}

View file

@ -56,21 +56,6 @@ unsigned int DPC_BUFBUSY_REG = 0;
unsigned int DPC_PIPEBUSY_REG = 0;
unsigned int DPC_TMEM_REG = 0;
unsigned int VI_STATUS_REG = 0;
unsigned int VI_ORIGIN_REG = 0;
unsigned int VI_WIDTH_REG = 0;
unsigned int VI_INTR_REG = 0;
unsigned int VI_V_CURRENT_LINE_REG = 0;
unsigned int VI_TIMING_REG = 0;
unsigned int VI_V_SYNC_REG = 0;
unsigned int VI_H_SYNC_REG = 0;
unsigned int VI_LEAP_REG = 0;
unsigned int VI_H_START_REG = 0;
unsigned int VI_V_START_REG = 0;
unsigned int VI_V_BURST_REG = 0;
unsigned int VI_X_SCALE_REG = 0;
unsigned int VI_Y_SCALE_REG = 0;
void dummy_check_interrupts() {}
RT64::UserConfiguration::Antialiasing compute_max_supported_aa(RT64::RenderSampleCounts bits) {
@ -250,20 +235,22 @@ zelda64::renderer::RT64Context::RT64Context(uint8_t* rdram, ultramodern::rendere
appCore.DPC_PIPEBUSY_REG = &DPC_PIPEBUSY_REG;
appCore.DPC_TMEM_REG = &DPC_TMEM_REG;
appCore.VI_STATUS_REG = &VI_STATUS_REG;
appCore.VI_ORIGIN_REG = &VI_ORIGIN_REG;
appCore.VI_WIDTH_REG = &VI_WIDTH_REG;
appCore.VI_INTR_REG = &VI_INTR_REG;
appCore.VI_V_CURRENT_LINE_REG = &VI_V_CURRENT_LINE_REG;
appCore.VI_TIMING_REG = &VI_TIMING_REG;
appCore.VI_V_SYNC_REG = &VI_V_SYNC_REG;
appCore.VI_H_SYNC_REG = &VI_H_SYNC_REG;
appCore.VI_LEAP_REG = &VI_LEAP_REG;
appCore.VI_H_START_REG = &VI_H_START_REG;
appCore.VI_V_START_REG = &VI_V_START_REG;
appCore.VI_V_BURST_REG = &VI_V_BURST_REG;
appCore.VI_X_SCALE_REG = &VI_X_SCALE_REG;
appCore.VI_Y_SCALE_REG = &VI_Y_SCALE_REG;
ultramodern::renderer::ViRegs* vi_regs = ultramodern::renderer::get_vi_regs();
appCore.VI_STATUS_REG = &vi_regs->VI_STATUS_REG;
appCore.VI_ORIGIN_REG = &vi_regs->VI_ORIGIN_REG;
appCore.VI_WIDTH_REG = &vi_regs->VI_WIDTH_REG;
appCore.VI_INTR_REG = &vi_regs->VI_INTR_REG;
appCore.VI_V_CURRENT_LINE_REG = &vi_regs->VI_V_CURRENT_LINE_REG;
appCore.VI_TIMING_REG = &vi_regs->VI_TIMING_REG;
appCore.VI_V_SYNC_REG = &vi_regs->VI_V_SYNC_REG;
appCore.VI_H_SYNC_REG = &vi_regs->VI_H_SYNC_REG;
appCore.VI_LEAP_REG = &vi_regs->VI_LEAP_REG;
appCore.VI_H_START_REG = &vi_regs->VI_H_START_REG;
appCore.VI_V_START_REG = &vi_regs->VI_V_START_REG;
appCore.VI_V_BURST_REG = &vi_regs->VI_V_BURST_REG;
appCore.VI_X_SCALE_REG = &vi_regs->VI_X_SCALE_REG;
appCore.VI_Y_SCALE_REG = &vi_regs->VI_Y_SCALE_REG;
// Set up the RT64 application configuration fields.
RT64::ApplicationConfiguration appConfig;
@ -338,9 +325,7 @@ void zelda64::renderer::RT64Context::send_dl(const OSTask* task) {
app->processDisplayLists(app->core.RDRAM, task->t.data_ptr & 0x3FFFFFF, 0, true);
}
void zelda64::renderer::RT64Context::update_screen(uint32_t vi_origin) {
VI_ORIGIN_REG = vi_origin;
void zelda64::renderer::RT64Context::update_screen() {
app->updateScreen();
}

View file

@ -900,6 +900,13 @@ void recompui::drop_files(const std::list<std::filesystem::path> &file_list) {
return;
}
recompui::set_config_tab(recompui::ConfigTab::Mods);
// If the config menu isn't open, open it in the mods tab.
if (!recompui::is_context_shown(recompui::get_config_context_id())) {
recompui::hide_all_contexts();
recompui::show_context(recompui::get_config_context_id(), "");
}
recompui::open_notification("Installing Mods", "Please Wait");
// TODO: Needs a progress callback and a prompt for every mod that needs to be confirmed to be overwritten.
// TODO: Run this on a background thread and use the callbacks to advance the state instead of blocking.