diff --git a/libultraship b/libultraship index 15d57d806..0833afad6 160000 --- a/libultraship +++ b/libultraship @@ -1 +1 @@ -Subproject commit 15d57d806e39d7f19783e26acc1a062d402169c7 +Subproject commit 0833afad66e96d2ec4bbc410186d7247dc243ee2 diff --git a/soh/CMakeLists.txt b/soh/CMakeLists.txt index ffc128bcd..228303558 100644 --- a/soh/CMakeLists.txt +++ b/soh/CMakeLists.txt @@ -328,7 +328,7 @@ endif() include(FetchContent) FetchContent_Declare( Boost - URL https://sourceforge.net/projects/boost/files/boost/1.81.0/boost_1_81_0.tar.gz + URL https://archives.boost.io/release/1.81.0/source/boost_1_81_0.tar.gz URL_HASH SHA256=205666dea9f6a7cfed87c7a6dfbeb52a2c1b9de55712c9c1a87735d7181452b6 SOURCE_SUBDIR "null" # Set to a nonexistent directory so boost is not built (we don't need to build it) DOWNLOAD_EXTRACT_TIMESTAMP false # supress timestamp warning, not needed since the url wont change diff --git a/soh/include/z64.h b/soh/include/z64.h index e668b2e7a..bad2e1ca7 100644 --- a/soh/include/z64.h +++ b/soh/include/z64.h @@ -744,7 +744,6 @@ typedef struct { /* 0x0134 */ char** doActionSegment; /* 0x0138 */ u8* iconItemSegment; /* 0x013C */ char** mapSegment; - char** mapSegmentName; /* 0x0140 */ u8 mapPalette[32]; /* 0x0160 */ DmaRequest dmaRequest_160; /* 0x0180 */ DmaRequest dmaRequest_180; @@ -815,6 +814,10 @@ typedef struct { /* 0x026C */ u8 dinsNayrus; // "m_magic"; din's fire and nayru's love /* 0x026D */ u8 all; // "another"; enables all item restrictions } restrictions; + // #region SOH [General] + /* */ char* mapSegmentName[2]; // Tracks the map segment texture by OTR sig name + /* */ u8 mapPalettesPulse[40][32]; // Used to have unique pointers per map pulse color for the shader backend. 40 for map pulse timer x2 + // #endregion } InterfaceContext; // size = 0x270 typedef struct { diff --git a/soh/soh/Enhancements/cosmetics/authenticGfxPatches.cpp b/soh/soh/Enhancements/cosmetics/authenticGfxPatches.cpp index de97f3840..709854c35 100644 --- a/soh/soh/Enhancements/cosmetics/authenticGfxPatches.cpp +++ b/soh/soh/Enhancements/cosmetics/authenticGfxPatches.cpp @@ -8,6 +8,7 @@ extern "C" { #include "objects/object_gi_soldout/object_gi_soldout.h" #include "objects/object_ik/object_ik.h" #include "objects/object_link_child/object_link_child.h" +#include "objects/object_ru2/object_ru2.h" uint32_t ResourceMgr_GameHasMasterQuest(); uint32_t ResourceMgr_GameHasOriginal(); @@ -187,10 +188,25 @@ void PatchIronKnuckleTextureOverflow() { } } +void PatchPrincessRutoEaring() { + // FAST3D: This is a hack for the issue of both TEXEL0 and TEXEL1 using the same texture with different settings. + // Ruto's earring uses both TEXEL0 and TEXEL1 to render. The issue is that it never loads anything into TEXEL1, so + // it reuses whatever happens to be there, which is the water temple brick texture. It just so happens that the + // earring texture loads into the same place in TMEM as the brick texture, so when it comes to rendering, TEXEL1 + // uses the earring texture with different clamp settings, and it displays without noticeable error. However, both + // texel samplers are not intended to be used for the same texture with different settings, so this misuse confuses + // our texture cache, and we load the wrong settings for the earrings texture. This patch is a hack that replaces + // TEXEL1 with TEXEL0, which is most likely the original intention, and all is well. + ResourceMgr_PatchGfxByName(gAdultRutoHeadDL, "RutoEaringTileFix", 162, + gsDPSetCombineLERP(TEXEL0, 0, PRIMITIVE, 0, TEXEL0, 0, ENVIRONMENT, 0, 0, 0, 0, COMBINED, + TEXEL0, 0, PRIM_LOD_FRAC, COMBINED)); +} + void ApplyAuthenticGfxPatches() { PatchDekuStickTextureOverflow(); PatchFreezardTextureOverflow(); PatchIronKnuckleTextureOverflow(); + PatchPrincessRutoEaring(); } // Patches the Sold Out GI DL to render the texture in the mirror boundary diff --git a/soh/soh/Enhancements/randomizer/3drando/location_access/locacc_fire_temple.cpp b/soh/soh/Enhancements/randomizer/3drando/location_access/locacc_fire_temple.cpp index 440483af6..ff2fb885c 100644 --- a/soh/soh/Enhancements/randomizer/3drando/location_access/locacc_fire_temple.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/location_access/locacc_fire_temple.cpp @@ -40,7 +40,7 @@ void AreaTable_Init_FireTemple() { }, { //Exits Entrance(FIRE_TEMPLE_FIRST_ROOM, {[]{return true;}}), - Entrance(FIRE_TEMPLE_BOSS_ENTRYWAY, {[]{return BossKeyFireTemple && ((IsAdult && LogicFireBossDoorJump) || CanUse(HOVER_BOOTS) || Here(FIRE_TEMPLE_FIRE_MAZE_UPPER, []{return CanUse(MEGATON_HAMMER);}));}}), + Entrance(FIRE_TEMPLE_BOSS_ENTRYWAY, {[]{return BossKeyFireTemple && ((IsAdult && (LogicFireBossDoorJump || Here(FIRE_TEMPLE_FIRE_MAZE_UPPER, []{return CanUse(MEGATON_HAMMER);}))) || CanUse(HOVER_BOOTS));}}), }); areaTable[FIRE_TEMPLE_LOOP_ENEMIES] = Area("Fire Temple Loop Enemies", "Fire Temple", FIRE_TEMPLE, NO_DAY_NIGHT_CYCLE, {}, {}, { diff --git a/soh/soh/Enhancements/randomizer/savefile.cpp b/soh/soh/Enhancements/randomizer/savefile.cpp index 29f2ee9a4..74d3853c4 100644 --- a/soh/soh/Enhancements/randomizer/savefile.cpp +++ b/soh/soh/Enhancements/randomizer/savefile.cpp @@ -207,6 +207,9 @@ extern "C" void Randomizer_InitSaveFile() { gSaveContext.randomizerInf[i] = 0; } + // Reset triforce pieces collected + gSaveContext.triforcePiecesCollected = 0; + gSaveContext.cutsceneIndex = 0; // no intro cutscene // Starts pending ice traps out at 0 before potentially incrementing them down the line. gSaveContext.pendingIceTrapCount = 0; @@ -442,8 +445,5 @@ extern "C" void Randomizer_InitSaveFile() { gSaveContext.itemGetInf[3] |= 0x8000; // Obtained Mask of Truth } - // Reset triforce pieces collected - gSaveContext.triforcePiecesCollected = 0; - SetStartingItems(); } diff --git a/soh/soh/OTRGlobals.cpp b/soh/soh/OTRGlobals.cpp index 63001a2b2..1045c98f8 100644 --- a/soh/soh/OTRGlobals.cpp +++ b/soh/soh/OTRGlobals.cpp @@ -307,7 +307,7 @@ OTRGlobals::OTRGlobals() { // tell LUS to reserve 3 SoH specific threads (Game, Audio, Save) context->InitResourceManager(OTRFiles, {}, 3); - + context->InitControlDeck({BTN_MODIFIER1, BTN_MODIFIER2}); context->GetControlDeck()->SetSinglePlayerMappingMode(true); @@ -317,7 +317,9 @@ OTRGlobals::OTRGlobals() { auto sohInputEditorWindow = std::make_shared("gControllerConfigurationEnabled", "Input Editor"); context->InitWindow(sohInputEditorWindow); context->InitAudio(); - + + SPDLOG_INFO("Starting Ship of Harkinian version {}", (char*)gBuildVersion); + context->GetResourceManager()->GetResourceLoader()->RegisterResourceFactory(LUS::ResourceType::SOH_Animation, "Animation", std::make_shared()); context->GetResourceManager()->GetResourceLoader()->RegisterResourceFactory(LUS::ResourceType::SOH_PlayerAnimation, "PlayerAnimation", std::make_shared()); context->GetResourceManager()->GetResourceLoader()->RegisterResourceFactory(LUS::ResourceType::SOH_Room, "Room", std::make_shared()); // Is room scene? maybe? @@ -2674,6 +2676,24 @@ extern "C" void Gfx_RegisterBlendedTexture(const char* name, u8* mask, u8* repla gfx_register_blended_texture(name, mask, replacement); } +extern "C" void Gfx_UnregisterBlendedTexture(const char* name) { + gfx_unregister_blended_texture(name); +} + +extern "C" void Gfx_TextureCacheDelete(const uint8_t* texAddr) { + char* imgName = (char*)texAddr; + + if (texAddr == nullptr) { + return; + } + + if (ResourceMgr_OTRSigCheck(imgName)) { + texAddr = (const uint8_t*)GetResourceDataByNameHandlingMQ(imgName); + } + + gfx_texture_cache_delete(texAddr); +} + void SoH_ProcessDroppedFiles(std::string filePath) { try { std::ifstream configStream(filePath); diff --git a/soh/soh/OTRGlobals.h b/soh/soh/OTRGlobals.h index 518aac8d1..db42341c2 100644 --- a/soh/soh/OTRGlobals.h +++ b/soh/soh/OTRGlobals.h @@ -176,6 +176,8 @@ void Entrance_InitEntranceTrackingData(void); void EntranceTracker_SetCurrentGrottoID(s16 entranceIndex); void EntranceTracker_SetLastEntranceOverride(s16 entranceIndex); void Gfx_RegisterBlendedTexture(const char* name, u8* mask, u8* replacement); +void Gfx_UnregisterBlendedTexture(const char* name); +void Gfx_TextureCacheDelete(const uint8_t* addr); void SaveManager_ThreadPoolWait(); void CheckTracker_OnMessageClose(); diff --git a/soh/soh/z_scene_otr.cpp b/soh/soh/z_scene_otr.cpp index 6926f313f..8bfb13308 100644 --- a/soh/soh/z_scene_otr.cpp +++ b/soh/soh/z_scene_otr.cpp @@ -149,6 +149,13 @@ bool Scene_CommandMeshHeader(PlayState* play, LUS::ISceneCommand* cmd) { extern "C" void* func_800982FC(ObjectContext* objectCtx, s32 bankIndex, s16 objectId); +bool OTRfunc_800982FC(ObjectContext* objectCtx, s32 bankIndex, s16 objectId) { + + objectCtx->status[bankIndex].id = -objectId; + + return false; +} + bool Scene_CommandObjectList(PlayState* play, LUS::ISceneCommand* cmd) { // LUS::SetObjectList* cmdObj = static_pointer_cast(cmd); LUS::SetObjectList* cmdObj = (LUS::SetObjectList*)cmd; @@ -164,49 +171,30 @@ bool Scene_CommandObjectList(PlayState* play, LUS::ISceneCommand* cmd) { void* nextPtr; k = 0; - // i = play->objectCtx.unk_09; - i = 0; + i = play->objectCtx.unk_09; firstStatus = &play->objectCtx.status[0]; status = &play->objectCtx.status[i]; - for (int i = 0; i < cmdObj->objects.size(); i++) { - bool alreadyIncluded = false; - - for (int j = 0; j < play->objectCtx.num; j++) { - if (play->objectCtx.status[j].id == cmdObj->objects[i]) { - alreadyIncluded = true; - break; + // Loop until a mismatch in the object lists + // Then clear all object ids past that in the context object list and kill actors for those objects + for (i = play->objectCtx.unk_09, k = 0; i < play->objectCtx.num; i++, k++) { + if (play->objectCtx.status[i].id != cmdObj->objects[k]) { + for (j = i; j < play->objectCtx.num; j++) { + play->objectCtx.status[j].id = OBJECT_INVALID; } - } - - if (!alreadyIncluded) { - play->objectCtx.status[play->objectCtx.num++].id = cmdObj->objects[i]; func_80031A28(play, &play->actorCtx); + break; } } - /* - while (i < play->objectCtx.num) { - if (status->id != *objectEntry) { - status2 = &play->objectCtx.status[i]; - for (j = i; j < play->objectCtx.num; j++) { - status2->id = OBJECT_INVALID; - status2++; - } - play->objectCtx.num = i; - func_80031A28(play, &play->actorCtx); - - continue; + // Continuing from the last index, add the remaining object ids from the command object list + for (; k < cmdObj->objects.size(); k++, i++) { + if (i < OBJECT_EXCHANGE_BANK_MAX - 1) { + OTRfunc_800982FC(&play->objectCtx, i, cmdObj->objects[k]); } - - i++; - k++; - objectEntry++; - status++; } play->objectCtx.num = i; - */ return false; } diff --git a/soh/src/code/z_actor.c b/soh/src/code/z_actor.c index cd17e1756..4b0ff576f 100644 --- a/soh/src/code/z_actor.c +++ b/soh/src/code/z_actor.c @@ -1239,8 +1239,7 @@ void Actor_Init(Actor* actor, PlayState* play) { CollisionCheck_InitInfo(&actor->colChkInfo); actor->floorBgId = BGCHECK_SCENE; ActorShape_Init(&actor->shape, 0.0f, NULL, 0.0f); - //if (Object_IsLoaded(&play->objectCtx, actor->objBankIndex)) - { + if (Object_IsLoaded(&play->objectCtx, actor->objBankIndex)) { //Actor_SetObjectDependency(play, actor); actor->init(actor, play); actor->init = NULL; @@ -2881,9 +2880,9 @@ s32 func_800314D4(PlayState* play, Actor* actor, Vec3f* arg2, f32 arg3) { 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)) { + if ((((fabsf(arg2->x) - actor->uncullZoneScale) * var) < 1.0f) && + (((arg2->y + actor->uncullZoneDownward) * var) > -1.0f) && + (((arg2->y - actor->uncullZoneScale) * var) < 1.0f)) { return true; } } @@ -3149,6 +3148,9 @@ void Actor_FreeOverlay(ActorDBEntry* dbEntry) { osSyncPrintf(VT_RST); } +// SoH: Flag to check if actors are being spawned from the actor entry list +// This flag is checked against to allow actors which dont have an objectBankIndex in the objectCtx slot/status array to spawn +// An example of what this fixes, is that it allows hookshot to be used as child int gMapLoading = 0; Actor* Actor_Spawn(ActorContext* actorCtx, PlayState* play, s16 actorId, f32 posX, f32 posY, f32 posZ, diff --git a/soh/src/code/z_bgcheck.c b/soh/src/code/z_bgcheck.c index 4acc68e55..7c99e7ca2 100644 --- a/soh/src/code/z_bgcheck.c +++ b/soh/src/code/z_bgcheck.c @@ -1902,7 +1902,7 @@ s32 BgCheck_CheckWallImpl(CollisionContext* colCtx, u16 xpFlags, Vec3f* posResul s32 bgId2; f32 nx, ny, nz; // unit normal of polygon - if (CVarGetInteger("gNoClip", 0) != 0) { + if (CVarGetInteger("gNoClip", 0) && actor != NULL && actor->id == ACTOR_PLAYER) { return false; } diff --git a/soh/src/code/z_map_exp.c b/soh/src/code/z_map_exp.c index c4db0098b..213ee018d 100644 --- a/soh/src/code/z_map_exp.c +++ b/soh/src/code/z_map_exp.c @@ -524,7 +524,6 @@ void Map_Init(PlayState* play) { interfaceCtx->unk_25A = -1; interfaceCtx->mapSegment = GAMESTATE_ALLOC_MC(&play->state, 2 * sizeof(char*)); - interfaceCtx->mapSegmentName = GAMESTATE_ALLOC_MC(&play->state, 2 * sizeof(char*)); // "MAP texture initialization scene_data_ID=%d mapSegment=%x" osSyncPrintf("\n\n\nMAP テクスチャ初期化 scene_data_ID=%d\nmapSegment=%x\n\n", play->sceneNum, interfaceCtx->mapSegment, play); diff --git a/soh/src/code/z_scene.c b/soh/src/code/z_scene.c index 1702dc3d9..eeff423d7 100644 --- a/soh/src/code/z_scene.c +++ b/soh/src/code/z_scene.c @@ -83,9 +83,10 @@ void Object_UpdateBank(ObjectContext* objectCtx) { 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]; @@ -96,10 +97,12 @@ void Object_UpdateBank(ObjectContext* objectCtx) { } else if (!osRecvMesg(&status->loadQueue, NULL, OS_MESG_NOBLOCK)) { status->id = -status->id; } + */ + status->id = -status->id; } status++; } - */ + } s32 Object_GetIndex(ObjectContext* objectCtx, s16 objectId) { 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 index b077d8ef7..30455e8e5 100644 --- a/soh/src/overlays/actors/ovl_Boss_Dodongo/z_boss_dodongo.c +++ b/soh/src/overlays/actors/ovl_Boss_Dodongo/z_boss_dodongo.c @@ -10,10 +10,6 @@ #include // malloc #include // memcpy -// OTRTODO: Replace usage of this method when we can clear the cache -// for a single texture without the need of a DL opcode in the render code -void gfx_texture_cache_clear(); - #define FLAGS (ACTOR_FLAG_TARGETABLE | ACTOR_FLAG_HOSTILE | ACTOR_FLAG_UPDATE_WHILE_CULLED | ACTOR_FLAG_DRAW_WHILE_CULLED) #define LAVA_TEX_WIDTH 32 @@ -123,7 +119,9 @@ void BossDodongo_RegisterBlendedLavaTextureUpdate() { sMaskTexLava[i] = maskVal; } } + Gfx_RegisterBlendedTexture(gDodongosCavernBossLavaFloorTex, sMaskTexLava, NULL); + Gfx_TextureCacheDelete(sMaskTexLava); return; } @@ -165,7 +163,9 @@ void BossDodongo_RegisterBlendedLavaTextureUpdate() { } } - gfx_texture_cache_clear(); + Gfx_TextureCacheDelete(sMaskTexLava); + Gfx_TextureCacheDelete(sLavaWavyTex); + Gfx_TextureCacheDelete(sLavaFloorModifiedTex); } void func_808C12C4(u8* arg1, s16 arg2) { @@ -228,6 +228,7 @@ void func_808C1554_Raw(void* arg0, void* floorTex, s32 arg2, f32 arg3) { } free(sp54); + Gfx_TextureCacheDelete(sLavaWavyTexRaw); } // Modified to support CPU modified texture with the resource system @@ -255,6 +256,8 @@ void func_808C1554(void* arg0, void* floorTex, s32 arg2, f32 arg3) { temp_s3[i + temp2] = sp54[i + i2]; } } + + Gfx_TextureCacheDelete(sLavaWavyTex); } void func_808C17C8(PlayState* play, Vec3f* arg1, Vec3f* arg2, Vec3f* arg3, f32 arg4, s16 arg5) { @@ -373,6 +376,13 @@ void BossDodongo_Init(Actor* thisx, PlayState* play) { Gfx_RegisterBlendedTexture(object_kingdodongo_Tex_016990, sMaskTex32x16, NULL); Gfx_RegisterBlendedTexture(object_kingdodongo_Tex_016E10, sMaskTex32x16, NULL); + // Clear cache for masks + Gfx_TextureCacheDelete(sMaskTex8x16); + Gfx_TextureCacheDelete(sMaskTex8x32); + Gfx_TextureCacheDelete(sMaskTex16x16); + Gfx_TextureCacheDelete(sMaskTex16x32); + Gfx_TextureCacheDelete(sMaskTex32x16); + BossDodongo_RegisterBlendedLavaTextureUpdate(); // Register alt listener to update the blended lava for the replacement texture based on alt path @@ -1206,6 +1216,7 @@ void BossDodongo_Update(Actor* thisx, PlayState* play2) { } } else { sMaskTexLava[new_var] = 1; + Gfx_TextureCacheDelete(sMaskTexLava); } this->unk_1C2 += 37; @@ -1345,18 +1356,6 @@ void BossDodongo_Draw(Actor* thisx, PlayState* play) { gSPInvalidateTexCache(POLY_OPA_DISP++, sMaskTex32x16); } - gSPInvalidateTexCache(POLY_OPA_DISP++, sMaskTexLava); - - // Using WORK_DISP to invalidate these textures as they are used in drawing the scene textures which happens - // before actors are drawn. WORK_DISP comes before POLAY_OPA_DISP. It is probably not meant for this, but it - // at least works for now. - // Alternatively, having a way to invalidate just these pointers from the Update func should be sufficient. - if (sLavaFloorModifiedTexRaw != NULL) { - gSPInvalidateTexCache(WORK_DISP++, sLavaWavyTexRaw); - } else { - gSPInvalidateTexCache(WORK_DISP++, sLavaWavyTex); - } - if ((this->unk_1C0 >= 2) && (this->unk_1C0 & 1)) { POLY_OPA_DISP = Gfx_SetFog(POLY_OPA_DISP, 255, 255, 255, 0, 900, 1099); } else { 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 index 5f3737b44..357b2c0ca 100644 --- a/soh/src/overlays/actors/ovl_Demo_Gj/z_demo_gj.c +++ b/soh/src/overlays/actors/ovl_Demo_Gj/z_demo_gj.c @@ -192,11 +192,11 @@ void DemoGj_Explode(DemoGj* this, PlayState* play, Vec3f* initialPos, Vec3f* dir phi_s0 = 0x21; } - Gfx* gfx = ResourceMgr_LoadGfxByName(gGanonRubbleDL); - + // SoH [Port] Changed from &gGanonsCastleRubbleAroundArenaDL[28] to gGanonRubbleDL as it seems this was an error in the original rom/decomp + // Other calls to EffectSsKakera_Spawn with OBJECT_GEFF use gGanonRubbleDL, so this change is to match that EffectSsKakera_Spawn(play, &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); + OBJECT_GEFF, gGanonRubbleDL); theta += 0x2AAA; } 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 index e1b65efca..be1143ff1 100644 --- a/soh/src/overlays/actors/ovl_En_Part/z_en_part.c +++ b/soh/src/overlays/actors/ovl_En_Part/z_en_part.c @@ -8,6 +8,8 @@ #include "objects/object_tite/object_tite.h" #include "objects/object_ik/object_ik.h" +#include // strcmp + #define FLAGS ACTOR_FLAG_UPDATE_WHILE_CULLED void EnPart_Init(Actor* thisx, PlayState* play); @@ -297,11 +299,11 @@ void EnPart_Draw(Actor* thisx, PlayState* play) { gSPSegment(POLY_OPA_DISP++, 0x08, func_80ACEAC0(play->state.gfxCtx, 255, 255, 255, 180, 180, 180)); gSPSegment(POLY_OPA_DISP++, 0x09, func_80ACEAC0(play->state.gfxCtx, 225, 205, 115, 25, 20, 0)); gSPSegment(POLY_OPA_DISP++, 0x0A, func_80ACEAC0(play->state.gfxCtx, 225, 205, 115, 25, 20, 0)); - } else if ((thisx->params == 9) && (this->displayList == ResourceMgr_LoadGfxByName(object_tite_DL_002FF0))) { + } else if ((thisx->params == 9) && (strcmp((const char*)this->displayList, object_tite_DL_002FF0) == 0)) { gSPSegment(POLY_OPA_DISP++, 0x08, object_tite_Tex_001300); gSPSegment(POLY_OPA_DISP++, 0x09, object_tite_Tex_001700); gSPSegment(POLY_OPA_DISP++, 0x0A, object_tite_Tex_001900); - } else if ((thisx->params == 10) && (this->displayList == ResourceMgr_LoadGfxByName(object_tite_DL_002FF0))) { + } else if ((thisx->params == 10) && (strcmp((const char*)this->displayList, object_tite_DL_002FF0) == 0)) { gSPSegment(POLY_OPA_DISP++, 0x08, object_tite_Tex_001B00); gSPSegment(POLY_OPA_DISP++, 0x09, object_tite_Tex_001F00); gSPSegment(POLY_OPA_DISP++, 0x0A, object_tite_Tex_002100); 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 index 6be42b42d..fe4a77724 100644 --- a/soh/src/overlays/actors/ovl_En_Ru2/z_en_ru2.c +++ b/soh/src/overlays/actors/ovl_En_Ru2/z_en_ru2.c @@ -821,19 +821,6 @@ void func_80AF3F20(EnRu2* this, PlayState* play) { void EnRu2_Draw(Actor* thisx, PlayState* play) { EnRu2* this = (EnRu2*)thisx; - // FAST3D: This is a hack for the issue of both TEXEL0 and TEXEL1 using the same texture with different settings. - // Ruto's earring uses both TEXEL0 and TEXEL1 to render. The issue is that it never loads anything into TEXEL1, so - // it reuses whatever happens to be there, which is the water temple brick texture. It just so happens that the - // earring texture loads into the same place in tmem as the brick texture, so when it comes to rendering, TEXEL1 - // uses the earring texture with diffrent clamp settings, and it displays without noticeable error. However, both - // texel samplers are not intended to be used for the same texture with different settings, so this misuse confuses - // our texture cache, and we load the wrong settings for the earrings texture. This patch is a hack that replaces - // TEXEL1 with TEXEL0, which is most likely the original intention, and all is well. - Gfx* gfx = ResourceMgr_LoadGfxByName(gAdultRutoHeadDL); - Gfx patch = gsDPSetCombineLERP(TEXEL0, 0, PRIMITIVE, 0, TEXEL0, 0, ENVIRONMENT, 0, 0, 0, 0, COMBINED, TEXEL0, 0, - PRIM_LOD_FRAC, COMBINED); - gfx[0xA2] = patch; - if ((this->drawConfig < 0) || (this->drawConfig >= ARRAY_COUNT(sDrawFuncs)) || (sDrawFuncs[this->drawConfig] == 0)) { // "Draw Mode is improper!" 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 index 28723d3d0..34dc47aa9 100644 --- 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 @@ -312,6 +312,9 @@ void KaleidoScope_DrawDungeonMap(PlayState* play, GraphicsContext* gfxCtx) { KaleidoScope_DrawQuadTextureRGBA32(gfxCtx, gQuestIconGoldSkulltulaTex, 24, 24, 8); } + // Unique index for both pulse phases + uint8_t palettePulseIdx = (mapBgPulseStage ? 40 : 20) - mapBgPulseTimer; + if ((play->sceneNum >= SCENE_DEKU_TREE) && (play->sceneNum <= SCENE_TREASURE_BOX_SHOP)) { stepR = (mapBgPulseR - mapBgPulseColors[mapBgPulseStage][0]) / mapBgPulseTimer; stepG = (mapBgPulseG - mapBgPulseColors[mapBgPulseStage][1]) / mapBgPulseTimer; @@ -324,6 +327,9 @@ void KaleidoScope_DrawDungeonMap(PlayState* play, GraphicsContext* gfxCtx) { interfaceCtx->mapPalette[28] = (rgba16 & 0xFF00) >> 8; interfaceCtx->mapPalette[29] = rgba16 & 0xFF; + interfaceCtx->mapPalettesPulse[palettePulseIdx][28] = (rgba16 & 0xFF00) >> 8; + interfaceCtx->mapPalettesPulse[palettePulseIdx][29] = rgba16 & 0xFF; + mapBgPulseTimer--; if (mapBgPulseTimer == 0) { mapBgPulseStage ^= 1; @@ -335,7 +341,8 @@ void KaleidoScope_DrawDungeonMap(PlayState* play, GraphicsContext* gfxCtx) { 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); + // Use a unique palette address per frame so the renderer/shader can cache all variations + gDPLoadTLUT_pal16(POLY_KAL_DISP++, 0, interfaceCtx->mapPalettesPulse[palettePulseIdx]); gDPSetTextureLUT(POLY_KAL_DISP++, G_TT_RGBA16); u8 mirroredWorld = CVarGetInteger("gMirroredWorld", 0); @@ -349,10 +356,6 @@ void KaleidoScope_DrawDungeonMap(PlayState* play, GraphicsContext* gfxCtx) { gSPVertex(POLY_KAL_DISP++, &pauseCtx->mapPageVtx[60], 8, 0); - // The dungeon map textures are recreated each frame, so always invalidate them - gSPInvalidateTexCache(POLY_KAL_DISP++, interfaceCtx->mapSegment[0]); - gSPInvalidateTexCache(POLY_KAL_DISP++, interfaceCtx->mapSegment[1]); - gDPLoadTextureBlock_4b(POLY_KAL_DISP++, interfaceCtx->mapSegmentName[0], G_IM_FMT_CI, MAP_48x85_TEX_WIDTH, MAP_48x85_TEX_HEIGHT, 0, G_TX_WRAP | mirrorMode, G_TX_WRAP | G_TX_NOMIRROR, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); 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 index b70197cc2..2da5d5b25 100644 --- 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 @@ -1204,8 +1204,6 @@ Gfx* KaleidoScope_DrawPageSections(Gfx* gfx, Vtx* vertices, void** textures) { return gfx; } -static uint8_t mapBlendMask[MAP_48x85_TEX_WIDTH * MAP_48x85_TEX_HEIGHT]; - void KaleidoScope_DrawPages(PlayState* play, GraphicsContext* gfxCtx) { static Color_RGB8 D_8082ACF4[12] = { { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 255, 255, 0 }, { 0, 0, 0 }, @@ -1374,10 +1372,6 @@ void KaleidoScope_DrawPages(PlayState* play, GraphicsContext* gfxCtx) { } } - // Need to invalidate the blend mask every frame. Ideally this would be done in KaleidoScope_DrawDungeonMap - // but the reference is not shared between files - gSPInvalidateTexCache(POLY_KAL_DISP++, mapBlendMask); - if (pauseCtx->pageIndex) { // pageIndex != PAUSE_ITEM gDPPipeSync(OVERLAY_DISP++); gDPSetCombineMode(OVERLAY_DISP++, G_CC_MODULATEIA, G_CC_MODULATEIA); @@ -3325,6 +3319,7 @@ static uint8_t mapLeftTexModified[MAP_48x85_TEX_SIZE]; static uint8_t mapRightTexModified[MAP_48x85_TEX_SIZE]; static uint8_t* mapLeftTexModifiedRaw = NULL; static uint8_t* mapRightTexModifiedRaw = NULL; +static uint8_t mapBlendMask[MAP_48x85_TEX_WIDTH * MAP_48x85_TEX_HEIGHT]; // Load dungeon maps into the interface context // SoH [General] - Modified to account for our resource system and HD textures @@ -3356,19 +3351,16 @@ void KaleidoScope_LoadDungeonMap(PlayState* play) { size_t size = (width * height) / 2; // account for CI4 size // Resource size being larger than the calculated CI size means it is most likely not a CI4 texture - // Abort early end undo the blended effect by clearing the mask to avoid crashing + // Abort early and unregister the blended effect to avoid crashing if (size < ResourceGetTexSizeByName(interfaceCtx->mapSegmentName[0])) { - if (mapBlendMask[0] != 0) { - for (size_t i = 0; i < ARRAY_COUNT(mapBlendMask); i++) { - mapBlendMask[i] = 0; - } - } - interfaceCtx->mapSegment[0] = NULL; interfaceCtx->mapSegment[1] = NULL; - Gfx_RegisterBlendedTexture(interfaceCtx->mapSegmentName[0], mapBlendMask, NULL); - Gfx_RegisterBlendedTexture(interfaceCtx->mapSegmentName[1], mapBlendMask, NULL); + Gfx_UnregisterBlendedTexture(interfaceCtx->mapSegmentName[0]); + Gfx_UnregisterBlendedTexture(interfaceCtx->mapSegmentName[1]); + + Gfx_TextureCacheDelete(interfaceCtx->mapSegmentName[0]); + Gfx_TextureCacheDelete(interfaceCtx->mapSegmentName[1]); return; } @@ -3403,6 +3395,11 @@ void KaleidoScope_LoadDungeonMap(PlayState* play) { Gfx_RegisterBlendedTexture(interfaceCtx->mapSegmentName[0], mapBlendMask, interfaceCtx->mapSegment[0]); Gfx_RegisterBlendedTexture(interfaceCtx->mapSegmentName[1], mapBlendMask, interfaceCtx->mapSegment[1]); + + Gfx_TextureCacheDelete(interfaceCtx->mapSegmentName[0]); + Gfx_TextureCacheDelete(interfaceCtx->mapSegmentName[1]); + Gfx_TextureCacheDelete(interfaceCtx->mapSegment[0]); + Gfx_TextureCacheDelete(interfaceCtx->mapSegment[1]); } static uint8_t registeredDungeonMapTextureHook = false; @@ -3443,6 +3440,11 @@ void KaleidoScope_UpdateDungeonMap(PlayState* play) { KaleidoScope_LoadDungeonMap(play); Map_SetFloorPalettesData(play, pauseCtx->dungeonMapSlot - 3); + // Copy the map palette values to all pulse palettes + for (uint8_t i = 0; i < ARRAY_COUNT(interfaceCtx->mapPalettesPulse); i++) { + memcpy(interfaceCtx->mapPalettesPulse[i], interfaceCtx->mapPalette, sizeof(interfaceCtx->mapPalette)); + } + s32 size = MAP_48x85_TEX_SIZE; if (ResourceMgr_TexIsRaw(interfaceCtx->mapSegmentName[0])) {