Compare commits

..

36 Commits

Author SHA1 Message Date
aMannus a62f8edc7f Bump Version 2023-03-31 22:48:04 +02:00
aMannus 8fc9fc18ec Rig treasure chest minigame 2023-03-28 15:00:11 +02:00
aMannus f2a975872e Change wide link into sideways link while climbing 2023-03-28 14:34:04 +02:00
aMannus b5b0990e83 Wide Link while climbing 2023-03-28 14:19:32 +02:00
aMannus af3ebf9a08 Super heavy Iron Boots 2023-03-28 14:13:16 +02:00
aMannus 68b3de6aac Double Hover Boots duration 2023-03-28 13:18:26 +02:00
aMannus eace543250 Forced Hyper Bosses 2023-03-28 13:06:03 +02:00
aMannus dc8374830c Bunny hood backwards speed 2023-03-28 10:54:19 +02:00
aMannus 6721c2d791 Random bonks while rolling 2023-03-28 10:49:38 +02:00
aMannus c90b32231a Random Cucco Storms 2023-03-28 10:46:50 +02:00
aMannus 85ca3c9ea9 5x boomerang length 2023-03-28 10:34:20 +02:00
aMannus 679beee4f3 Random hookshot/longshot length 2023-03-28 10:32:17 +02:00
aMannus c18603d1c9 Slightly smaller Link 2023-03-28 10:10:24 +02:00
aMannus ae910355c6 Fake 999 prices with shops/scrubs 2023-03-28 10:00:17 +02:00
aMannus d7febc01b4 Randomly unequip c-buttons, shield, unequip ocarina on dpad down 2023-03-28 09:48:47 +02:00
aMannus 9088f0c38d Slow mweep 2023-03-28 09:46:49 +02:00
aMannus e8e8997fd8 Spicy dampe 2023-03-28 09:46:49 +02:00
aMannus 3a5ef24396 Hardcore enemy rando 2023-03-28 09:43:49 +02:00
Malkierian e76b990c8a Randomzier: Fix starting rupee item collection (#2645)
* When rupees are given as starting items, they still modify `gSaveContext.rupeeAccumulator`, which means that if you make a new file, start it, and reload without saving, or exit SoH before starting the new file, those rupees were lost. This adds a check for `gPlayState` being NULL, and if it is, adds those initial rupees to the rupee count directly.

* Update soh/src/code/z_parameter.c

Co-authored-by: briaguya <70942617+briaguya-ai@users.noreply.github.com>

---------

Co-authored-by: briaguya <70942617+briaguya-ai@users.noreply.github.com>
2023-03-22 20:59:08 -04:00
Malkierian 533ee9379a Balanced Shopsanity Bugfix (#2640)
* Fixes settings bug with Balanced shopsanity prices from Shopsanity Affordable Update.

* Wrong location

* Updated fixed comment
2023-03-15 22:46:30 -04:00
briaguya 72b12890fd bump version (#2632) 2023-03-14 09:16:54 -04:00
briaguya 0ade1aad69 use LUS with atmosphere fix (#2629) 2023-03-14 03:56:14 -04:00
Malkierian 264623f40a Shopsanity Affordable Logic Update (#2617)
* Modified Affordable logic to select randomly from 10, 105, 205, and 505 depending on the selected wallet tier. Updated the tooltip to reflect.

* Clarified logic in comments and variable names.

* Streamlined affordable check for starter wallet.

One more function comment.

* More streamlining of affordable price generation, comment clarification.
2023-03-13 17:24:51 -04:00
Christopher Leggett d9008938f8 Makes sequenceMap and seqLoadStatus a dynamic size (#2610)
Refactors to allow the above two arrays to be a dynamic size when the game launches, size is set during the AudioLoad_Init function.
2023-03-12 15:55:25 -04:00
Adam Bird 1ce9634f65 clear bongo bongo static effect on actor reset (#2603) 2023-03-12 08:06:52 +01:00
Adam Bird 39acd71fc4 remove left over boss location logic from mq logic (#2619) 2023-03-12 08:01:18 +01:00
aMannus 095066ffcd Enemy Rando - Arwing range check (Khan Edition) (#2613)
* Arwing range check

* Small code cleanup
2023-03-11 09:47:56 -08:00
aMannus 1c00d56053 Exclude club moblins in clear rooms (#2593) 2023-03-08 13:10:14 -05:00
Adam Bird 7c558ae089 fix rando quest selection falling back to vanilla saves (#2599) 2023-03-08 13:09:22 -05:00
Adam Bird 60f4f71495 fix flag wind speed to use correct uint type (#2600) 2023-03-08 13:08:54 -05:00
Adam Bird 2117d98178 fix skulltula token count message on vanilla (#2597) 2023-03-08 10:18:02 -05:00
Malkierian 5d32343919 Modify cutsceneIndex in scene transition autosave to include use-cases outside of normal vanilla play. (#2588) 2023-03-06 23:17:27 -05:00
Adam Bird ce115272cd fix equip now enhancement running with non-equipment items (#2590) 2023-03-06 23:15:51 -05:00
Adam Bird 0a67e42f6c handle angry happy mask throwing link out in entrance randomizer (#2578) 2023-03-04 00:30:20 -05:00
Adam Bird a5bf135541 fix change age cheat causing link to spawn in the wrong spot (#2573) 2023-03-02 17:12:46 -05:00
Josh Bodner 0d54cb15df Spot fix so autosave dropdown will save immediately (#2568) 2023-03-02 16:57:55 -05:00
39 changed files with 335 additions and 182 deletions
+2 -2
View File
@@ -5,8 +5,8 @@ set(CMAKE_CXX_STANDARD 20 CACHE STRING "The C++ standard to use")
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Minimum OS X deployment version" FORCE)
project(Ship VERSION 6.1.0 LANGUAGES C CXX)
set(PROJECT_BUILD_NAME "KHAN ALFA" CACHE STRING "")
project(Ship VERSION 42.0.69 LANGUAGES C CXX)
set(PROJECT_BUILD_NAME "GREG ALFA" CACHE STRING "")
set(PROJECT_TEAM "github.com/harbourmasters" CACHE STRING "")
set_property(DIRECTORY ${CMAKE_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT soh)
+3 -2
View File
@@ -22,7 +22,8 @@
#define CALC_RESAMPLE_FREQ(sampleRate) ((float)sampleRate / (s32)gAudioContext.audioBufferParameters.frequency)
#define MAX_SEQUENCES 0x400
//#define MAX_SEQUENCES 0x800
extern size_t sequenceMapSize;
extern char* fontMap[256];
@@ -917,7 +918,7 @@ typedef struct {
/* 0x342C */ AudioPoolSplit3 temporaryCommonPoolSplit;
/* 0x3438 */ u8 sampleFontLoadStatus[0x30];
/* 0x3468 */ u8 fontLoadStatus[0x30];
/* 0x3498 */ u8 seqLoadStatus[MAX_SEQUENCES];
/* 0x3498 */ u8* seqLoadStatus;
/* 0x3518 */ volatile u8 resetStatus;
/* 0x3519 */ u8 audioResetSpecIdToLoad;
/* 0x351C */ s32 audioResetFadeOutFramesLeft;
+4 -40
View File
@@ -11,55 +11,17 @@ extern "C" {
extern "C" uint32_t ResourceMgr_IsSceneMasterQuest(s16 sceneNum);
static EnemyEntry randomizedEnemySpawnTable[RANDOMIZED_ENEMY_SPAWN_TABLE_SIZE] = {
{ ACTOR_EN_FIREFLY, 2 }, // Regular Keese
{ ACTOR_EN_FIREFLY, 1 }, // Fire Keese
{ ACTOR_EN_FIREFLY, 4 }, // Ice Keese
{ ACTOR_EN_TEST, 2 }, // Stalfos
{ ACTOR_EN_TITE, -1 }, // Tektite (red)
{ ACTOR_EN_TITE, -2 }, // Tektite (blue)
{ ACTOR_EN_WALLMAS, 1 }, // Wallmaster
{ ACTOR_EN_DODONGO, -1 }, // Dodongo
{ ACTOR_EN_PEEHAT, -1 }, // Flying Peahat (big grounded, doesn't spawn larva)
{ ACTOR_EN_PEEHAT, 1 }, // Flying Peahat Larva
{ ACTOR_EN_ZF, -1 }, // Lizalfos
{ ACTOR_EN_ZF, -2 }, // Dinolfos
{ ACTOR_EN_GOMA, 7 }, // Gohma larva (non-gohma rooms)
{ ACTOR_EN_BUBBLE, 0 }, // Shabom (bubble)
{ ACTOR_EN_DODOJR, 0 }, // Baby Dodongo
{ ACTOR_EN_TORCH2, 0 }, // Dark Link
{ ACTOR_EN_BILI, 0 }, // Biri (jellyfish)
{ ACTOR_EN_TP, -1 }, // Electric Tailparasan
{ ACTOR_EN_ST, 0 }, // Skulltula (normal)
{ ACTOR_EN_ST, 1 }, // Skulltula (big)
{ ACTOR_EN_ST, 2 }, // Skulltula (invisible)
{ ACTOR_EN_BW, 0 }, // Torch Slug
{ ACTOR_EN_EIYER, 10 }, // Stinger (land) (One in formation, sink under floor and do not activate)
{ ACTOR_EN_MB, 0 }, // Moblins (Club)
{ ACTOR_EN_DEKUBABA, 0 }, // Deku Baba (small)
{ ACTOR_EN_DEKUBABA, 1 }, // Deku Baba (large)
{ ACTOR_EN_AM, -1 }, // Armos (enemy variant)
{ ACTOR_EN_DEKUNUTS, 768 }, // Mad Scrub (triple attack) (projectiles don't work)
{ ACTOR_EN_VALI, -1 }, // Bari (big jellyfish)
{ ACTOR_EN_BB, -1 }, // Bubble (flying skull enemy) (blue)
{ ACTOR_EN_YUKABYUN, 0 }, // Flying Floor Tile
{ ACTOR_EN_VM, 1280 }, // Beamos
{ ACTOR_EN_FLOORMAS, 0 }, // Floormaster
{ ACTOR_EN_RD, 1 }, // Redead (standing)
{ ACTOR_EN_RD, 32766 }, // Gibdo (standing)
{ ACTOR_EN_SB, 0 }, // Shell Blade
{ ACTOR_EN_KAREBABA, 0 }, // Withered Deku Baba
{ ACTOR_EN_RR, 0 }, // Like-Like
{ ACTOR_EN_NY, 0 }, // Spike (rolling enemy)
{ ACTOR_EN_IK, 2 }, // Iron Knuckle (black, standing)
{ ACTOR_EN_IK, 3 }, // Iron Knuckle (white, standing)
{ ACTOR_EN_TUBO_TRAP, 0 }, // Flying pot
{ ACTOR_EN_FZ, 0 }, // Freezard
{ ACTOR_EN_CLEAR_TAG, 1 }, // Arwing
{ ACTOR_EN_WF, 0 }, // Wolfos (normal)
{ ACTOR_EN_WF, 1 }, // Wolfos (white)
{ ACTOR_EN_SKB, 1 }, // Stalchild (small)
{ ACTOR_EN_SKB, 20 }, // Stalchild (big)
{ ACTOR_EN_CROW, 0 } // Guay
{ ACTOR_EN_CLEAR_TAG, 1 } // Arwing
// Doesn't work {ACTOR_EN_POH, 0}, // Poe (Seems to rely on other objects?)
// Doesn't work {ACTOR_EN_POH, 2}, // Poe (composer Sharp) (Seems to rely on other objects?)
@@ -319,9 +281,11 @@ bool IsEnemyAllowedToSpawn(int16_t sceneNum, int8_t roomNum, EnemyEntry enemy) {
// Shell Blade & Spike - Child link can't kill these with sword or deku stick.
// Arwing & Dark Link - Both go out of bounds way too easily, softlocking the player.
// Wallmaster - Not easily visible, often makes players think they're softlocked and that there's no enemies left.
// Club Moblin - Many issues with them falling or placing out of bounds. Maybe fixable in the future?
bool enemiesToExcludeClearRooms = enemy.id == ACTOR_EN_FZ || enemy.id == ACTOR_EN_VM || enemy.id == ACTOR_EN_SB ||
enemy.id == ACTOR_EN_NY || enemy.id == ACTOR_EN_CLEAR_TAG ||
enemy.id == ACTOR_EN_WALLMAS || enemy.id == ACTOR_EN_TORCH2;
enemy.id == ACTOR_EN_WALLMAS || enemy.id == ACTOR_EN_TORCH2 ||
enemy.id == ACTOR_EN_MB;
// Bari - Spawns 3 more enemies, potentially extremely difficult in timed rooms.
bool enemiesToExcludeTimedRooms = enemiesToExcludeClearRooms || enemy.id == ACTOR_EN_VALI;
+1 -1
View File
@@ -7,7 +7,7 @@ typedef struct EnemyEntry {
int16_t params;
} EnemyEntry;
#define RANDOMIZED_ENEMY_SPAWN_TABLE_SIZE 49
#define RANDOMIZED_ENEMY_SPAWN_TABLE_SIZE 11
bool IsEnemyFoundToRandomize(int16_t sceneNum, int8_t roomNum, int16_t actorId, int16_t params, float posX);
bool IsEnemyAllowedToSpawn(int16_t sceneNum, int8_t roomNum, EnemyEntry enemy);
@@ -34,6 +34,7 @@ uint8_t GameInteractor_ReverseControlsActive();
int32_t GameInteractor_DefenseModifier();
int32_t GameInteractor_RunSpeedModifier();
GIGravityLevel GameInteractor_GravityLevel();
uint8_t GameInteractor_SecondCollisionUpdate();
#ifdef __cplusplus
}
#endif
@@ -66,6 +67,7 @@ public:
static int32_t DefenseModifier;
static int32_t RunSpeedModifier;
static GIGravityLevel GravityLevel;
static uint8_t SecondCollisionUpdate;
static void SetPacifistMode(bool active);
};
@@ -90,6 +92,7 @@ public:
DEFINE_HOOK(OnReceiveItem, void(u8 item));
DEFINE_HOOK(OnSceneInit, void(s16 sceneNum));
DEFINE_HOOK(OnPlayerUpdate, void());
DEFINE_HOOK(OnActorUpdate, void(void* actor));
DEFINE_HOOK(OnSaveFile, void(int32_t fileNum));
DEFINE_HOOK(OnLoadFile, void(int32_t fileNum));
@@ -116,6 +119,7 @@ public:
static void GiveDekuShield();
static void SpawnCuccoStorm();
static void ForceInterfaceUpdate();
static void UpdateActor(void* refActor);
static GameInteractionEffectQueryResult SpawnEnemyWithOffset(uint32_t enemyId, int32_t enemyParams);
};
@@ -30,6 +30,10 @@ void GameInteractor_ExecuteOnPlayerUpdate() {
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnPlayerUpdate>();
}
void GameInteractor_ExecuteOnActorUpdate(void* actor) {
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnActorUpdate>(actor);
}
// MARK: - Save Files
void GameInteractor_ExecuteOnSaveFile(int32_t fileNum) {
@@ -7,6 +7,7 @@ extern "C" void GameInteractor_ExecuteOnGameFrameUpdate();
extern "C" void GameInteractor_ExecuteOnReceiveItemHooks(u8 item);
extern "C" void GameInteractor_ExecuteOnSceneInit(s16 sceneNum);
extern "C" void GameInteractor_ExecuteOnPlayerUpdate();
extern "C" void GameInteractor_ExecuteOnActorUpdate(void* actor);
// MARK: - Save Files
extern "C" void GameInteractor_ExecuteOnSaveFile(int32_t fileNum);
@@ -142,6 +142,27 @@ void GameInteractor::RawAction::ForceInterfaceUpdate() {
Interface_Update(gPlayState);
}
void GameInteractor::RawAction::UpdateActor(void* refActor) {
// Update actor again outside of their normal update cycle.
Actor* actor = static_cast<Actor*>(refActor);
// Sometimes the actor is destroyed in the previous Update, so check if the update function still exists.
if (actor->update != NULL) {
// Fix for enemies sometimes taking a "fake" hit, where their invincibility timer is
// reset but damage isn't applied.
if (actor->colorFilterTimer > 0) {
actor->colorFilterTimer--;
}
// This variable is used to not let the collider subscribe a second time when the actor update function
// is ran a second time, incorrectly applying double damage in some cases.
GameInteractor::State::SecondCollisionUpdate = 1;
actor->update(actor, gPlayState);
GameInteractor::State::SecondCollisionUpdate = 0;
}
}
GameInteractionEffectQueryResult GameInteractor::RawAction::SpawnEnemyWithOffset(uint32_t enemyId, int32_t enemyParams) {
if (!GameInteractor::CanSpawnEnemy()) {
@@ -12,6 +12,7 @@ bool GameInteractor::State::ReverseControlsActive = 0;
int32_t GameInteractor::State::DefenseModifier = 0;
int32_t GameInteractor::State::RunSpeedModifier = 0;
GIGravityLevel GameInteractor::State::GravityLevel = GI_GRAVITY_LEVEL_NORMAL;
uint8_t GameInteractor::State::SecondCollisionUpdate = 0;
void GameInteractor::State::SetPacifistMode(bool active) {
PacifistModeActive = active;
@@ -79,3 +80,8 @@ int32_t GameInteractor_RunSpeedModifier() {
GIGravityLevel GameInteractor_GravityLevel() {
return GameInteractor::State::GravityLevel;
}
// MARK: - GameInteractor::State::SecondCollisionUpdate
uint8_t GameInteractor_SecondCollisionUpdate() {
return GameInteractor::State::SecondCollisionUpdate;
}
+78 -43
View File
@@ -6,11 +6,9 @@ extern "C" {
#include <z64.h>
#include "macros.h"
#include "variables.h"
#include "functions.h"
extern SaveContext gSaveContext;
extern PlayState* gPlayState;
extern void Play_PerformSave(PlayState* play);
extern s32 Health_ChangeBy(PlayState* play, s16 healthChange);
extern void Rupees_ChangeBy(s16 rupeeChange);
}
void RegisterInfiniteMoney() {
@@ -89,13 +87,13 @@ void RegisterInfiniteNayrusLove() {
void RegisterMoonJumpOnL() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
if (!gPlayState) return;
if (CVarGetInteger("gMoonJumpOnL", 0) != 0) {
if (gPlayState) {
Player* player = GET_PLAYER(gPlayState);
Player* player = GET_PLAYER(gPlayState);
if (CHECK_BTN_ANY(gPlayState->state.input[0].cur.button, BTN_L)) {
player->actor.velocity.y = 6.34375f;
}
if (CHECK_BTN_ANY(gPlayState->state.input[0].cur.button, BTN_L)) {
player->actor.velocity.y = 6.34375f;
}
}
});
@@ -104,23 +102,23 @@ void RegisterMoonJumpOnL() {
void RegisterInfiniteISG() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
if (!gPlayState) return;
if (CVarGetInteger("gEzISG", 0) != 0) {
if (gPlayState) {
Player* player = GET_PLAYER(gPlayState);
player->swordState = 1;
}
Player* player = GET_PLAYER(gPlayState);
player->swordState = 1;
}
});
}
void RegisterUnrestrictedItems() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
if (!gPlayState) return;
if (CVarGetInteger("gNoRestrictItems", 0) != 0) {
if (gPlayState) {
u8 sunsBackup = gPlayState->interfaceCtx.restrictions.sunsSong;
memset(&gPlayState->interfaceCtx.restrictions, 0, sizeof(gPlayState->interfaceCtx.restrictions));
gPlayState->interfaceCtx.restrictions.sunsSong = sunsBackup;
}
u8 sunsBackup = gPlayState->interfaceCtx.restrictions.sunsSong;
memset(&gPlayState->interfaceCtx.restrictions, 0, sizeof(gPlayState->interfaceCtx.restrictions));
gPlayState->interfaceCtx.restrictions.sunsSong = sunsBackup;
}
});
}
@@ -142,35 +140,31 @@ void RegisterFreezeTime() {
/// Switches Link's age and respawns him at the last entrance he entered.
void RegisterSwitchAge() {
bool warped = false;
Vec3f playerPos;
int16_t playerYaw;
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([&warped, &playerPos, &playerYaw]() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
static bool warped = false;
static Vec3f playerPos;
static int16_t playerYaw;
if (!gPlayState) return;
if (CVarGetInteger("gSwitchAge", 0) != 0) {
CVarSetInteger("gSwitchAge", 0);
if (gPlayState) {
playerPos = GET_PLAYER(gPlayState)->actor.world.pos;
playerYaw = GET_PLAYER(gPlayState)->actor.shape.rot.y;
gPlayState->nextEntranceIndex = gSaveContext.entranceIndex;
gPlayState->sceneLoadFlag = 0x14;
gPlayState->fadeTransition = 11;
gSaveContext.nextTransitionType = 11;
warped = true;
if (gPlayState->linkAgeOnLoad == 1) {
gPlayState->linkAgeOnLoad = 0;
} else {
gPlayState->linkAgeOnLoad = 1;
}
}
playerPos = GET_PLAYER(gPlayState)->actor.world.pos;
playerYaw = GET_PLAYER(gPlayState)->actor.shape.rot.y;
gPlayState->nextEntranceIndex = gSaveContext.entranceIndex;
gPlayState->sceneLoadFlag = 0x14;
gPlayState->fadeTransition = 11;
gSaveContext.nextTransitionType = 11;
gPlayState->linkAgeOnLoad ^= 1;
warped = true;
}
if (gPlayState) {
if (warped && gPlayState->sceneLoadFlag != 0x0014 && gSaveContext.nextTransitionType == 255) {
GET_PLAYER(gPlayState)->actor.shape.rot.y = playerYaw;
GET_PLAYER(gPlayState)->actor.world.pos = playerPos;
warped = false;
}
if (warped && gPlayState->sceneLoadFlag != 0x0014 && gSaveContext.nextTransitionType == 255) {
GET_PLAYER(gPlayState)->actor.shape.rot.y = playerYaw;
GET_PLAYER(gPlayState)->actor.world.pos = playerPos;
warped = false;
}
});
}
@@ -261,6 +255,46 @@ void RegisterRupeeDash() {
});
}
void RegisterHyperBosses() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnActorUpdate>([](void* refActor) {
// Run the update function a second time to make bosses move and act twice as fast.
Player* player = GET_PLAYER(gPlayState);
Actor* actor = static_cast<Actor*>(refActor);
uint8_t isBossActor =
actor->id == ACTOR_BOSS_GOMA || // Gohma
actor->id == ACTOR_BOSS_DODONGO || // King Dodongo
actor->id == ACTOR_BOSS_VA || // Barinade
actor->id == ACTOR_BOSS_GANONDROF || // Phantom Ganon
(actor->id == 0 && actor->category == ACTORCAT_BOSS) || // Phantom Ganon/Ganondorf Energy Ball/Thunder
actor->id == ACTOR_EN_FHG || // Phantom Ganon's Horse
actor->id == ACTOR_BOSS_FD || actor->id == ACTOR_BOSS_FD2 || // Volvagia (grounded/flying)
actor->id == ACTOR_BOSS_MO || // Morpha
actor->id == ACTOR_BOSS_SST || // Bongo Bongo
actor->id == ACTOR_BOSS_TW || // Twinrova
actor->id == ACTOR_BOSS_GANON || // Ganondorf
actor->id == ACTOR_BOSS_GANON2; // Ganon
// Don't apply during cutscenes because it causes weird behaviour and/or crashes on some bosses.
if (isBossActor && !Player_InBlockingCsMode(gPlayState, player)) {
// Barinade needs to be updated in sequence to avoid unintended behaviour.
if (actor->id == ACTOR_BOSS_VA) {
// params -1 is BOSSVA_BODY
if (actor->params == -1) {
Actor* actorList = gPlayState->actorCtx.actorLists[ACTORCAT_BOSS].head;
while (actorList != NULL) {
GameInteractor::RawAction::UpdateActor(actorList);
actorList = actorList->next;
}
}
} else {
GameInteractor::RawAction::UpdateActor(actor);
}
}
});
}
void InitMods() {
RegisterInfiniteMoney();
RegisterInfiniteHealth();
@@ -274,4 +308,5 @@ void InitMods() {
RegisterSwitchAge();
RegisterRupeeDash();
RegisterAutoSave();
RegisterHyperBosses();
}
@@ -233,16 +233,9 @@ void AreaTable_Init_DekuTree() {
Entrance(DEKU_TREE_MQ_BASEMENT_WATER_ROOM_BACK, {[]{return (IsChild && CanUse(KOKIRI_SWORD)) || CanUseProjectile || (Nuts && (IsChild && CanUse(STICKS)));}}),
});
areaTable[DEKU_TREE_MQ_BASEMENT_LEDGE] = Area("Deku Tree MQ Basement Ledge", "Deku Tree", DEKU_TREE, NO_DAY_NIGHT_CYCLE, {
//Events
EventAccess(&DekuTreeClear, {[]{return DekuTreeClear || (Here(DEKU_TREE_MQ_BASEMENT_LEDGE, []{return HasFireSourceWithTorch;}) &&
Here(DEKU_TREE_MQ_BASEMENT_LEDGE, []{return HasShield;}) &&
(IsAdult || KokiriSword || Sticks) && (Nuts || CanUse(SLINGSHOT) || CanUse(BOW) || HookshotOrBoomerang));}}),
}, {
areaTable[DEKU_TREE_MQ_BASEMENT_LEDGE] = Area("Deku Tree MQ Basement Ledge", "Deku Tree", DEKU_TREE, NO_DAY_NIGHT_CYCLE, {}, {
//Locations
LocationAccess(DEKU_TREE_MQ_DEKU_SCRUB, {[]{return CanStunDeku;}}),
LocationAccess(DEKU_TREE_QUEEN_GOHMA_HEART, {[]{return HasFireSourceWithTorch && HasShield && (IsAdult || KokiriSword || Sticks) && (Nuts || CanUse(SLINGSHOT) || CanUse(BOW) || HookshotOrBoomerang);}}),
LocationAccess(QUEEN_GOHMA, {[]{return HasFireSourceWithTorch && HasShield && (IsAdult || KokiriSword || Sticks) && (Nuts || CanUse(SLINGSHOT) || CanUse(BOW) || HookshotOrBoomerang);}}),
LocationAccess(DEKU_TREE_MQ_DEKU_SCRUB, {[]{return CanStunDeku;}}),
}, {
//Exits
Entrance(DEKU_TREE_MQ_BASEMENT_BACK_ROOM, {[]{return IsChild;}}),
@@ -291,13 +291,9 @@ void AreaTable_Init_DodongosCavern() {
areaTable[DODONGOS_CAVERN_MQ_BOSS_AREA] = Area("Dodongos Cavern MQ BossArea", "Dodongos Cavern", DODONGOS_CAVERN, NO_DAY_NIGHT_CYCLE, {
//Events
EventAccess(&FairyPot, {[]{return true;}}),
EventAccess(&DodongosCavernClear, {[]{return DodongosCavernClear || (CanBlastOrSmash && (Bombs || GoronBracelet) && (IsAdult || Sticks || KokiriSword));}}),
}, {
//Locations
LocationAccess(DODONGOS_CAVERN_MQ_UNDER_GRAVE_CHEST, {[]{return true;}}),
LocationAccess(DODONGOS_CAVERN_BOSS_ROOM_CHEST, {[]{return true;}}),
LocationAccess(DODONGOS_CAVERN_KING_DODONGO_HEART, {[]{return CanBlastOrSmash && (Bombs || GoronBracelet) && (IsAdult || Sticks || KokiriSword);}}),
LocationAccess(KING_DODONGO, {[]{return CanBlastOrSmash && (Bombs || GoronBracelet) && (IsAdult || Sticks || KokiriSword);}}),
LocationAccess(DODONGOS_CAVERN_MQ_GS_BACK_AREA, {[]{return true;}}),
}, {
//Exits
@@ -128,31 +128,39 @@ static constexpr std::array<double, 60> ShopPriceProbability= {
0.959992180, 0.968187000, 0.975495390, 0.981884488, 0.987344345, 0.991851853, 0.995389113, 0.997937921, 0.999481947, 1.000000000,
};
std::map<uint8_t, int> affordableCaps = {
{RO_SHOPSANITY_PRICE_STARTER, 10},
{RO_SHOPSANITY_PRICE_ADULT, 105},
{RO_SHOPSANITY_PRICE_GIANT, 205},
{RO_SHOPSANITY_PRICE_TYCOON, 505},
};
// If affordable option is on, cap items at affordable price just above the max of the previous wallet tier
int CapPriceAffordable(int value, int cap) {
if (Settings::ShopsanityPricesAffordable.Is(true) && value > cap)
return cap;
return value;
}
// Generate random number from 5 to wallet max
int GetPriceFromMax(int max) {
int temp = Random(1, max) * 5; // random range of 1 - wallet max / 5, where wallet max is the highest it goes as a multiple of 5
return CapPriceAffordable(temp, affordableCaps.find(Settings::ShopsanityPrices.Value<uint8_t>())->second);
return Random(1, max) * 5; // random range of 1 - wallet max / 5, where wallet max is the highest it goes as a multiple of 5
}
// Get random price out of available "affordable prices", or just return 10 if Starter wallet is selected (no need to randomly select
// from a single element)
int GetPriceAffordable() {
if (Settings::ShopsanityPrices.Is(RO_SHOPSANITY_PRICE_STARTER)) {
return 10;
}
static const std::vector<int> affordablePrices = { 10, 105, 205, 505 };
std::vector<int> priceList;
uint8_t maxElements = Settings::ShopsanityPrices.Value<uint8_t>();
for (int i = 0; i < maxElements; i++) {
priceList.push_back(affordablePrices.at(i));
}
return RandomElement(priceList);
}
int GetRandomShopPrice() {
// If Shopsanity prices aren't Balanced, but Affordable is on, don't GetPriceFromMax
if (Settings::ShopsanityPricesAffordable.Is(true) && Settings::ShopsanityPrices.IsNot(RO_SHOPSANITY_PRICE_BALANCED)) {
return GetPriceAffordable();
}
// max 0 means Balanced is selected, and thus shouldn't trigger GetPriceFromMax
int max = 0;
if(Settings::ShopsanityPrices.Is(RO_SHOPSANITY_PRICE_STARTER)) {// check for xx wallet setting and set max amount as method for
max = 19; // 95/5 // setting true randomization
// check settings for a wallet tier selection and set max amount as method for setting true randomization
if(Settings::ShopsanityPrices.Is(RO_SHOPSANITY_PRICE_STARTER)) {
max = 19; // 95/5
}
else if (Settings::ShopsanityPrices.Is(RO_SHOPSANITY_PRICE_ADULT)) {
max = 40; // 200/5
@@ -3094,7 +3094,7 @@ void DrawRandoEditor(bool& open) {
static const char* randoLinksPocket[4] = { "Dungeon Reward", "Advancement", "Anything", "Nothing" };
static const char* randoShuffleSongs[3] = { "Song Locations", "Dungeon Rewards", "Anywhere" };
static const char* randoShopsanity[7] = { "Off", "0 Items", "1 Item", "2 Items", "3 Items", "4 Items", "Random" };
static const char* randoShopsanityPrices[6] = { "Balanced", "Starter Wallet", "Adult Wallet", "Giant's Wallet", "Tycoon's Wallet", "Affordable" };
static const char* randoShopsanityPrices[5] = { "Balanced", "Starter Wallet", "Adult Wallet", "Giant's Wallet", "Tycoon's Wallet" };
static const char* randoTokensanity[4] = { "Off", "Dungeons", "Overworld", "All Tokens" };
static const char* randoShuffleScrubs[4] = { "Off", "Affordable", "Expensive", "Random Prices" };
static const char* randoShuffleMerchants[3] = { "Off", "On (no hints)", "On (with hints)" };
@@ -3729,8 +3729,8 @@ void DrawRandoEditor(bool& open) {
UIWidgets::EnhancementCheckbox(Settings::ShopsanityPricesAffordable.GetName().c_str(), "gRandomizeShopsanityPricesAffordable",
CVarGetInteger("gRandomizeShopsanityPrices", RO_SHOPSANITY_PRICE_BALANCED) == RO_SHOPSANITY_PRICE_BALANCED,
"This can only apply to a wallet range.");
UIWidgets::InsertHelpHoverText("Cap item prices to a value just above the previous tier wallet's max value.\n"
"Affordable caps: starter = 10, adult = 105, giant = 205, tycoon = 505\n"
UIWidgets::InsertHelpHoverText("Random selection between the selected wallet tier's affordable price and the affordable prices of the preceding wallet tiers.\n\n"
"Affordable prices per tier: starter = 10, adult = 105, giant = 205, tycoon = 505\n\n"
"Use this to enable wallet tier locking, but make shop items not as expensive as they could be.");
}
@@ -4654,7 +4654,7 @@ CustomMessageEntry Randomizer::GetMerchantMessage(RandomizerInf randomizerInf, u
} else {
shopItemName = EnumToSpoilerfileGetName[shopItemGet];
}
u16 shopItemPrice = merchantPrices[rc];
u16 shopItemPrice = 999;
if (textId == TEXT_SCRUB_RANDOM && shopItemPrice == 0) {
messageEntry = CustomMessageManager::Instance->RetrieveMessage(Randomizer::merchantMessageTableID, TEXT_SCRUB_RANDOM_FREE);
+2 -11
View File
@@ -834,18 +834,9 @@ namespace GameMenuBar {
UIWidgets::PaddedSeparator(false, true);
// Autosave enum value of 1 is the default in presets and the old checkbox "on" state for backwards compatibility
const uint16_t selectedAutosaveId = CVarGetInteger("gAutosave", 0);
std::string autosaveLabels[] = { "Off", "New Location + Major Item", "New Location + Any Item", "New Location", "Major Item", "Any Item" };
UIWidgets::PaddedText("Autosave", false, true);
if (ImGui::BeginCombo("##AutosaveComboBox", autosaveLabels[selectedAutosaveId].c_str())) {
for (int index = 0; index < sizeof(autosaveLabels) / sizeof(autosaveLabels[0]); index++) {
if (ImGui::Selectable(autosaveLabels[index].c_str(), index == selectedAutosaveId)) {
CVarSetInteger("gAutosave", index);
}
}
ImGui::EndCombo();
}
const char* autosaveLabels[] = { "Off", "New Location + Major Item", "New Location + Any Item", "New Location", "Major Item", "Any Item" };
UIWidgets::EnhancementCombobox("gAutosave", autosaveLabels, (sizeof(autosaveLabels) / sizeof(autosaveLabels[0])), CVarGetInteger("gAutosave", 0));
UIWidgets::Tooltip("Automatically save the game every time a new area is entered and/or item is obtained\n"
"Major items exclude rupees and health/magic/ammo refills (but include bombchus unless bombchu drops are enabled)");
+1 -1
View File
@@ -1678,7 +1678,7 @@ extern "C" int CustomMessage_RetrieveIfExists(PlayState* play) {
textId = TEXT_GS_FREEZE;
}
messageEntry = CustomMessageManager::Instance->RetrieveMessage(customMessageTableID, textId);
CustomMessageManager::ReplaceStringInMessage(messageEntry, "{{gsCount}}", std::to_string(gSaveContext.inventory.gsTokens + 1));
CustomMessageManager::ReplaceStringInMessage(messageEntry, "{{gsCount}}", std::to_string(gSaveContext.inventory.gsTokens));
}
}
if (textId == TEXT_HEART_CONTAINER && CVarGetInteger("gInjectItemCounts", 0)) {
+2
View File
@@ -1726,6 +1726,8 @@ void SaveManager::DeleteZeldaFile(int fileNum) {
}
fileMetaInfo[fileNum].valid = false;
fileMetaInfo[fileNum].randoSave = false;
fileMetaInfo[fileNum].requiresMasterQuest = false;
fileMetaInfo[fileNum].requiresOriginal = false;
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnDeleteFile>(fileNum);
}
@@ -37,7 +37,7 @@ void Ship::SetWindSettingsFactoryV0::ParseFileBinary(std::shared_ptr<BinaryReade
setWind->settings.windWest = reader->ReadInt8();
setWind->settings.windVertical = reader->ReadInt8();
setWind->settings.windSouth = reader->ReadInt8();
setWind->settings.windSpeed = reader->ReadInt8();
setWind->settings.windSpeed = reader->ReadUByte();
}
} // namespace Ship
@@ -12,7 +12,7 @@ typedef struct {
int8_t windWest;
int8_t windVertical;
int8_t windSouth;
int8_t windSpeed;
uint8_t windSpeed;
} WindSettings;
class SetWindSettings : public SceneCommand {
+1 -1
View File
@@ -53,7 +53,7 @@ void AudioHeap_ResetLoadStatus(void) {
}
}
for (i = 0; i < MAX_SEQUENCES; i++) {
for (i = 0; i < sequenceMapSize; i++) {
if (gAudioContext.seqLoadStatus[i] != 5) {
gAudioContext.seqLoadStatus[i] = 0;
}
+8 -4
View File
@@ -77,7 +77,8 @@ void* sUnusedHandler = NULL;
s32 gAudioContextInitalized = false;
char* sequenceMap[MAX_SEQUENCES];
char** sequenceMap;
size_t sequenceMapSize;
// A map of authentic sequence IDs to their cache policies, for use with sequence swapping.
u8 seqCachePolicyMap[MAX_AUTHENTIC_SEQID];
char* fontMap[256];
@@ -488,7 +489,7 @@ u8* AudioLoad_GetFontsForSequence(s32 seqId, u32* outNumFonts) {
return NULL;
u16 newSeqId = AudioEditor_GetReplacementSeq(seqId);
if (newSeqId > MAX_SEQUENCES || !sequenceMap[newSeqId]) {
if (newSeqId > sequenceMapSize || !sequenceMap[newSeqId]) {
return NULL;
}
SequenceData sDat = ResourceMgr_LoadSeqByName(sequenceMap[newSeqId]);
@@ -1342,7 +1343,12 @@ void AudioLoad_Init(void* heap, size_t heapSize) {
AudioHeap_ResetStep();
int seqListSize = 0;
int customSeqListSize = 0;
char** seqList = ResourceMgr_ListFiles("audio/sequences*", &seqListSize);
char** customSeqList = ResourceMgr_ListFiles("custom/music/*", &customSeqListSize);
sequenceMapSize = (size_t)(seqListSize + customSeqListSize);
sequenceMap = malloc(sequenceMapSize * sizeof(char*));
gAudioContext.seqLoadStatus = malloc(sequenceMapSize * sizeof(char*));
for (size_t i = 0; i < seqListSize; i++)
{
@@ -1357,9 +1363,7 @@ void AudioLoad_Init(void* heap, size_t heapSize) {
free(seqList);
int customSeqListSize = 0;
int startingSeqNum = MAX_AUTHENTIC_SEQID; // 109 is the highest vanilla sequence
char** customSeqList = ResourceMgr_ListFiles("custom/music/*", &customSeqListSize);
qsort(customSeqList, customSeqListSize, sizeof(char*), strcmp_sort);
for (size_t i = startingSeqNum; i < startingSeqNum + customSeqListSize; i++) {
+1 -1
View File
@@ -3,7 +3,7 @@
#include <libultraship/libultra.h>
#include "global.h"
extern char* sequenceMap[MAX_SEQUENCES];
extern char** sequenceMap;
#define PORTAMENTO_IS_SPECIAL(x) ((x).mode & 0x80)
#define PORTAMENTO_MODE(x) ((x).mode & ~0x80)
+2
View File
@@ -8,6 +8,7 @@
#include "objects/object_bdoor/object_bdoor.h"
#include "soh/frame_interpolation.h"
#include "soh/Enhancements/enemyrandomizer.h"
#include "soh/Enhancements/game-interactor/GameInteractor.h"
#if defined(_MSC_VER) || defined(__GNUC__)
#include <string.h>
@@ -2595,6 +2596,7 @@ void Actor_UpdateAll(PlayState* play, ActorContext* actorCtx) {
actor->colorFilterTimer--;
}
actor->update(actor, play);
GameInteractor_ExecuteOnActorUpdate(actor, play);
func_8003F8EC(play, &play->colCtx.dyna, actor);
}
+35
View File
@@ -1,6 +1,7 @@
#include "global.h"
#include "vt.h"
#include "overlays/effects/ovl_Effect_Ss_HitMark/z_eff_ss_hitmark.h"
#include "soh/Enhancements/game-interactor/GameInteractor.h"
typedef s32 (*ColChkResetFunc)(PlayState*, Collider*);
typedef void (*ColChkBloodFunc)(PlayState*, Collider*, Vec3f*);
@@ -1177,6 +1178,10 @@ static ColChkResetFunc sATResetFuncs[] = {
s32 CollisionCheck_SetAT(PlayState* play, CollisionCheckContext* colChkCtx, Collider* collider) {
s32 index;
if (GameInteractor_SecondCollisionUpdate()) {
return -1;
}
if (FrameAdvance_IsEnabled(play) == true) {
return -1;
}
@@ -1206,9 +1211,15 @@ s32 CollisionCheck_SetAT(PlayState* play, CollisionCheckContext* colChkCtx, Coll
s32 CollisionCheck_SetAT_SAC(PlayState* play, CollisionCheckContext* colChkCtx, Collider* collider,
s32 index) {
ASSERT(collider->shape <= COLSHAPE_QUAD);
if (GameInteractor_SecondCollisionUpdate()) {
return -1;
}
if (FrameAdvance_IsEnabled(play) == true) {
return -1;
}
sATResetFuncs[collider->shape](play, collider);
if (collider->actor != NULL && collider->actor->update == NULL) {
return -1;
@@ -1246,6 +1257,10 @@ static ColChkResetFunc sACResetFuncs[] = {
s32 CollisionCheck_SetAC(PlayState* play, CollisionCheckContext* colChkCtx, Collider* collider) {
s32 index;
if (GameInteractor_SecondCollisionUpdate()) {
return -1;
}
if (FrameAdvance_IsEnabled(play) == true) {
return -1;
}
@@ -1275,9 +1290,15 @@ s32 CollisionCheck_SetAC(PlayState* play, CollisionCheckContext* colChkCtx, Coll
s32 CollisionCheck_SetAC_SAC(PlayState* play, CollisionCheckContext* colChkCtx, Collider* collider,
s32 index) {
ASSERT(collider->shape <= COLSHAPE_QUAD);
if (GameInteractor_SecondCollisionUpdate()) {
return -1;
}
if (FrameAdvance_IsEnabled(play) == true) {
return -1;
}
sACResetFuncs[collider->shape](play, collider);
if (collider->actor != NULL && collider->actor->update == NULL) {
return -1;
@@ -1315,6 +1336,10 @@ static ColChkResetFunc sOCResetFuncs[] = {
s32 CollisionCheck_SetOC(PlayState* play, CollisionCheckContext* colChkCtx, Collider* collider) {
s32 index;
if (GameInteractor_SecondCollisionUpdate()) {
return -1;
}
if (FrameAdvance_IsEnabled(play) == true) {
return -1;
}
@@ -1345,9 +1370,15 @@ s32 CollisionCheck_SetOC(PlayState* play, CollisionCheckContext* colChkCtx, Coll
*/
s32 CollisionCheck_SetOC_SAC(PlayState* play, CollisionCheckContext* colChkCtx, Collider* collider,
s32 index) {
if (GameInteractor_SecondCollisionUpdate()) {
return -1;
}
if (FrameAdvance_IsEnabled(play) == true) {
return -1;
}
ASSERT(collider->shape <= COLSHAPE_QUAD);
sOCResetFuncs[collider->shape](play, collider);
if (collider->actor != NULL && collider->actor->update == NULL) {
@@ -1380,6 +1411,10 @@ s32 CollisionCheck_SetOC_SAC(PlayState* play, CollisionCheckContext* colChkCtx,
s32 CollisionCheck_SetOCLine(PlayState* play, CollisionCheckContext* colChkCtx, OcLine* collider) {
s32 index;
if (GameInteractor_SecondCollisionUpdate()) {
return -1;
}
if (FrameAdvance_IsEnabled(play) == true) {
return -1;
}
+5 -1
View File
@@ -3084,7 +3084,11 @@ s32 Health_ChangeBy(PlayState* play, s16 healthChange) {
}
void Rupees_ChangeBy(s16 rupeeChange) {
gSaveContext.rupeeAccumulator += rupeeChange;
if (gPlayState == NULL) {
gSaveContext.rupees += rupeeChange;
} else {
gSaveContext.rupeeAccumulator += rupeeChange;
}
if (rupeeChange > 0) {
gSaveContext.sohStats.count[COUNT_RUPEES_COLLECTED] += rupeeChange;
+1 -1
View File
@@ -874,7 +874,7 @@ void Play_Update(PlayState* play) {
// Also don't save when you first load a file to prevent consumables like magic from being lost
// Also don't save if there's a pending shop sale to prevent getting the item for a discount!
if ((CVarGetInteger("gAutosave", 0) >= 1) && (CVarGetInteger("gAutosave", 0) <= 3) &&
(gSaveContext.cutsceneIndex == 0) && (play->gameplayFrames > 60) && (gSaveContext.pendingSale == ITEM_NONE) &&
(gSaveContext.cutsceneIndex < 0xFFF0) && (play->gameplayFrames > 60) && (gSaveContext.pendingSale == ITEM_NONE) &&
(play->sceneNum != SCENE_YOUSEI_IZUMI_TATE) && (play->sceneNum != SCENE_KAKUSIANA) && (play->sceneNum != SCENE_KENJYANOMA)) {
Play_PerformSave(play);
}
+1 -3
View File
@@ -298,9 +298,7 @@ void Sram_InitSave(FileChooseContext* fileChooseCtx) {
gSaveContext.playerName[offset] = Save_GetSaveMetaInfo(fileChooseCtx->buttonIndex)->playerName[offset];
}
if (fileChooseCtx->questType[fileChooseCtx->buttonIndex] == 2 && strnlen(CVarGetString("gSpoilerLog", ""), 1) != 0 &&
!((Save_GetSaveMetaInfo(fileChooseCtx->buttonIndex)->requiresMasterQuest && !ResourceMgr_GameHasMasterQuest()) ||
(Save_GetSaveMetaInfo(fileChooseCtx->buttonIndex)->requiresMasterQuest && !ResourceMgr_GameHasOriginal()))) {
if (fileChooseCtx->questType[fileChooseCtx->buttonIndex] == 2 && strnlen(CVarGetString("gSpoilerLog", ""), 1) != 0) {
// Set N64DD Flags for save file
fileChooseCtx->n64ddFlags[fileChooseCtx->buttonIndex] = 1;
fileChooseCtx->n64ddFlag = 1;
@@ -84,8 +84,15 @@ void ArmsHook_Destroy(Actor* thisx, PlayState* play) {
void ArmsHook_Wait(ArmsHook* this, PlayState* play) {
if (this->actor.parent == NULL) {
Player* player = GET_PLAYER(play);
s32 length;
if (player->heldItemAction == PLAYER_IA_HOOKSHOT) {
length = Rand_ZeroOne() * 2 * 13;
} else {
length = Rand_ZeroOne() * 2 * 26;
}
// get correct timer length for hookshot or longshot
s32 length = ((player->heldItemAction == PLAYER_IA_HOOKSHOT) ? 13 : 26) * CVarGetFloat("gCheatHookshotReachMultiplier", 1.0f);
//s32 length = ((player->heldItemAction == PLAYER_IA_HOOKSHOT) ? 13 : 26) * CVarGetFloat("gCheatHookshotReachMultiplier", 1.0f);
ArmsHook_SetupAction(this, ArmsHook_Shoot);
func_8002D9A4(&this->actor, 20.0f);
@@ -35,7 +35,7 @@ void BossDodongo_DrawEffects(PlayState* play);
void BossDodongo_UpdateEffects(PlayState* play);
const ActorInit Boss_Dodongo_InitVars = {
ACTOR_EN_DODONGO,
ACTOR_BOSS_DODONGO,
ACTORCAT_BOSS,
FLAGS,
OBJECT_KINGDODONGO,
@@ -247,7 +247,7 @@ const ActorInit Boss_Sst_InitVars = {
(ActorFunc)BossSst_Destroy,
(ActorFunc)BossSst_UpdateHand,
(ActorFunc)BossSst_DrawHand,
NULL,
(ActorResetFunc)BossSst_Reset,
};
#include "z_boss_sst_colchk.c"
@@ -3268,4 +3268,13 @@ void BossSst_Reset(void) {
sCutsceneCamera= 0;
sBodyStatic = false;
// Reset death colors
sBodyColor.a = 255;
sBodyColor.r = 255;
sBodyColor.g = 255;
sBodyColor.b = 255;
sStaticColor.a = 255;
sStaticColor.r = 0;
sStaticColor.g = 0;
sStaticColor.b = 0;
}
@@ -136,7 +136,11 @@ void EnChanger_Init(Actor* thisx, PlayState* play2) {
this->rightChestGetItemId = GI_DOOR_KEY;
rightChestItem = ITEM_ETC_KEY_SMALL_CHEST_GAME;
if (Rand_ZeroFloat(1.99f) < 1.0f) {
uint8_t roomNum = play->roomCtx.curRoom.num;
uint8_t leftSideKey = roomNum == 1 || roomNum == 2 || roomNum == 5;
// Solution: LLRRL
if (leftSideKey) {
rightChestParams = (sLoserGetItemIds[play->roomCtx.curRoom.num] << 5) | 0x4000;
this->rightChestNum = new_var;
this->rightChestGetItemId = sLoserGetItemIds[play->roomCtx.curRoom.num];
@@ -475,9 +475,13 @@ void EnClearTag_Update(Actor* thisx, PlayState* play2) {
Math_ApproachS(&this->actor.world.rot.z, 0, 15, this->targetDirection.z);
Math_ApproachF(&this->targetDirection.z, 0x500, 1.0f, 0x100);
// Introduce a range requirement in Enemy Rando so Arwings don't shoot the player from
// across the map. Especially noticeable in big maps like Lake Hylia and Hyrule Field.
uint8_t enemyRandoShootLaser = !CVarGetInteger("gRandomizedEnemies", 0) || this->actor.xzDistToPlayer < 1000.0f;
// Check if the Arwing should fire its laser.
if ((this->frameCounter % 4) == 0 && (Rand_ZeroOne() < 0.75f) &&
(this->state == CLEAR_TAG_STATE_TARGET_LOCKED)) {
(this->state == CLEAR_TAG_STATE_TARGET_LOCKED) && enemyRandoShootLaser) {
this->shouldShootLaser = true;
}
} else {
@@ -6,6 +6,7 @@
#include "z_en_elf.h"
#include "objects/gameplay_keep/gameplay_keep.h"
#include "overlays/actors/ovl_En_Niw/z_en_niw.h"
#define FLAGS (ACTOR_FLAG_4 | ACTOR_FLAG_5 | ACTOR_FLAG_25)
@@ -1431,6 +1432,32 @@ void func_80A053F0(Actor* thisx, PlayState* play) {
} else if (!(this->fairyFlags & 0x80)) {
gSaveContext.naviTimer = 0;
}
// Unequip current shield randomly. Averages once every 1000 frames AKA 1000/20 = 50 seconds.
if (Rand_ZeroOne() < 0.001f) {
player->currentShield = PLAYER_SHIELD_NONE;
}
// Unequip current equipped C and dpad buttons. Averages once every 10000 frames AKA 10000/20 = 500 seconds.
if (Rand_ZeroOne() < 0.0001f) {
gSaveContext.equips.buttonItems[1] = gSaveContext.equips.buttonItems[2] =
gSaveContext.equips.buttonItems[3] = ITEM_NONE;
gSaveContext.equips.buttonItems[4] = gSaveContext.equips.buttonItems[5] =
gSaveContext.equips.buttonItems[6] = gSaveContext.equips.buttonItems[7] = ITEM_NONE;
}
// Unequip ocarina when set to dpad down.
if (gSaveContext.equips.buttonItems[5] == ITEM_OCARINA_FAIRY ||
gSaveContext.equips.buttonItems[5] == ITEM_OCARINA_TIME) {
gSaveContext.equips.buttonItems[5] = ITEM_NONE;
}
// Randomly spawn a cucco storm. Averages once every 12500 frames AKA 12500/20 = 625 seconds.
if (Rand_ZeroOne() < 0.00008f) {
EnNiw* cucco = (EnNiw*)Actor_Spawn(&play->actorCtx, gPlayState, ACTOR_EN_NIW, player->actor.world.pos.x,
player->actor.world.pos.y + 2200, player->actor.world.pos.z, 0, 0, 0, 0, 0);
cucco->actionFunc = func_80AB70A0_nocutscene;
}
}
}
+2 -2
View File
@@ -301,7 +301,7 @@ s32 EnKz_FollowPath(EnKz* this, PlayState* play) {
pathDiffZ = pointPos->z - this->actor.world.pos.z;
Math_SmoothStepToS(&this->actor.world.rot.y, (Math_FAtan2F(pathDiffX, pathDiffZ) * (0x8000 / M_PI)), 0xA, 0x3E8, 1);
if ((SQ(pathDiffX) + SQ(pathDiffZ)) < 10.0f * CVarGetInteger("gMweepSpeed", 1)) {
if ((SQ(pathDiffX) + SQ(pathDiffZ)) < 10.0f * 0.2f) {
this->waypoint++;
if (this->waypoint >= path->count) {
this->waypoint = 0;
@@ -413,7 +413,7 @@ void EnKz_SetupMweep(EnKz* this, PlayState* play) {
initPos.z += 260.0f;
Play_CameraSetAtEye(play, this->cutsceneCamera, &pos, &initPos);
func_8002DF54(play, &this->actor, 8);
this->actor.speedXZ = 0.1f * CVarGetInteger("gMweepSpeed", 1);
this->actor.speedXZ = 0.1f * 0.2f;
this->actionFunc = EnKz_Mweep;
}
@@ -13,6 +13,7 @@
#include "objects/object_mastergolon/object_mastergolon.h"
#include "objects/object_masterzoora/object_masterzoora.h"
#include "objects/object_masterkokirihead/object_masterkokirihead.h"
#include "soh/Enhancements/randomizer/randomizer_entrance.h"
#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_3 | ACTOR_FLAG_4)
@@ -934,7 +935,12 @@ void EnOssan_State_StartConversation(EnOssan* this, PlayState* play, Player* pla
EnOssan_TryPaybackMask(this, play);
return;
case OSSAN_HAPPY_STATE_ANGRY:
play->nextEntranceIndex = 0x1D1;
// In ER, handle happy mask throwing link out with not enough rupees
if (gSaveContext.n64ddFlag && Randomizer_GetSettingValue(RSK_SHUFFLE_ENTRANCES)) {
play->nextEntranceIndex = Entrance_OverrideNextIndex(0x1D1);
} else {
play->nextEntranceIndex = 0x1D1;
}
play->sceneLoadFlag = 0x14;
play->fadeTransition = 0x2E;
return;
@@ -189,12 +189,14 @@ void EnPoRelay_Race(EnPoRelay* this, PlayState* play) {
Vec3f vec;
f32 speed;
f32 multiplier;
int16_t minFireTimer = 8.0f;
f32 chanceForFire = 1.0f;
if (this->actionTimer != 0) {
this->actionTimer--;
}
if (this->actionTimer == 0 && Rand_ZeroOne() < 0.03f) {
this->actionTimer = 32;
if (this->actionTimer == 0 && Rand_ZeroOne() < chanceForFire) {
this->actionTimer = minFireTimer;
if (this->pathIndex < 23) {
speed = Rand_ZeroOne() * 3.0f;
if (speed < 1.0f) {
+14 -16
View File
@@ -103,6 +103,11 @@ void func_80AFB768(EnSi* this, PlayState* play) {
if (gSaveContext.n64ddFlag) {
Randomizer_UpdateSkullReward(this, play);
if (getItemId != RG_ICE_TRAP) {
Randomizer_GiveSkullReward(this, play);
} else {
gSaveContext.pendingIceTrapCount++;
}
} else {
Item_Give(play, giveItemId);
}
@@ -117,14 +122,8 @@ void func_80AFB768(EnSi* this, PlayState* play) {
Message_StartTextbox(play, textId, NULL);
if (gSaveContext.n64ddFlag) {
if (getItemId != RG_ICE_TRAP) {
Randomizer_GiveSkullReward(this, play);
Audio_PlayFanfare_Rando(getItem);
} else {
gSaveContext.pendingIceTrapCount++;
Audio_PlayFanfare(NA_BGM_SMALL_ITEM_GET);
}
if (gSaveContext.n64ddFlag && getItemId != RG_ICE_TRAP) {
Audio_PlayFanfare_Rando(getItem);
} else {
Audio_PlayFanfare(NA_BGM_SMALL_ITEM_GET);
}
@@ -149,20 +148,19 @@ void func_80AFB89C(EnSi* this, PlayState* play) {
if (!CHECK_FLAG_ALL(this->actor.flags, ACTOR_FLAG_13)) {
if (gSaveContext.n64ddFlag) {
Randomizer_UpdateSkullReward(this, play);
if (getItemId != RG_ICE_TRAP) {
Randomizer_GiveSkullReward(this, play);
} else {
gSaveContext.pendingIceTrapCount++;
}
} else {
Item_Give(play, giveItemId);
}
Message_StartTextbox(play, textId, NULL);
if (gSaveContext.n64ddFlag) {
if (getItemId != RG_ICE_TRAP) {
Randomizer_GiveSkullReward(this, play);
Audio_PlayFanfare_Rando(getItem);
} else {
gSaveContext.pendingIceTrapCount++;
Audio_PlayFanfare(NA_BGM_SMALL_ITEM_GET);
}
if (gSaveContext.n64ddFlag && getItemId != RG_ICE_TRAP) {
Audio_PlayFanfare_Rando(getItem);
} else {
Audio_PlayFanfare(NA_BGM_SMALL_ITEM_GET);
}
@@ -2785,7 +2785,7 @@ s32 func_808359FC(Player* this, PlayState* play) {
this->boomerangActor = &boomerang->actor;
if (boomerang != NULL) {
boomerang->moveTo = this->unk_664;
boomerang->returnTimer = 20;
boomerang->returnTimer = 100;
this->stateFlags1 |= PLAYER_STATE1_25;
if (!func_8008E9C4(this)) {
func_808355DC(this);
@@ -7734,7 +7734,7 @@ void func_80842180(Player* this, PlayState* play) {
}
if (CVarGetInteger("gMMBunnyHood", 0) && this->currentMask == PLAYER_MASK_BUNNY) {
sp2C *= 1.5f;
sp2C *= -6.0f;
}
if (CVarGetInteger("gEnableWalkModify", 0)) {
@@ -8654,8 +8654,10 @@ void func_80844708(Player* this, PlayState* play) {
func_8083A060(this, play);
}
} else {
f32 rand = Rand_ZeroOne();
uint8_t randomBonk = (rand <= .05);
if (this->linearVelocity >= 7.0f) {
if (((this->actor.bgCheckFlags & 0x200) && (D_8085360C < 0x2000)) ||
if (randomBonk || ((this->actor.bgCheckFlags & 0x200) && (D_8085360C < 0x2000)) ||
((this->cylinder.base.ocFlags1 & OC1_HIT) &&
(cylinderOc = this->cylinder.base.oc,
((cylinderOc->id == ACTOR_EN_WOOD02) &&
@@ -9935,7 +9937,7 @@ s32 func_80847A78(Player* this) {
if (this->actor.bgCheckFlags & 1) {
if (!cond) {
this->hoverBootsTimer = 19;
this->hoverBootsTimer = 38;
}
return 0;
}
@@ -11028,6 +11030,26 @@ void Player_Update(Actor* thisx, PlayState* play) {
break;
}
if (this->stateFlags1 & PLAYER_STATE1_21) {
this->actor.shape.rot.x = 31000;
this->actor.shape.rot.z = 16000;
} else if (this->actor.shape.rot.x == 31000 || this->actor.shape.rot.z == 16000) {
this->actor.shape.rot.x = 0;
this->actor.shape.rot.z = 0;
}
if (this->actor.scale.x == 0.01f) {
this->actor.scale.x = 0.009f;
}
if (this->actor.scale.y == 0.01f) {
this->actor.scale.y = 0.009f;
}
if (this->actor.scale.z == 0.01f) {
this->actor.scale.z = 0.009f;
}
switch (GameInteractor_GravityLevel()) {
case GI_GRAVITY_LEVEL_HEAVY:
this->actor.gravity = -4.0f;
@@ -11038,6 +11060,10 @@ void Player_Update(Actor* thisx, PlayState* play) {
default:
break;
}
if (this->currentBoots == PLAYER_BOOTS_IRON) {
this->actor.gravity = -10.0f;
}
GameInteractor_ExecuteOnPlayerUpdate();
}
@@ -11098,9 +11124,9 @@ void Player_DrawGameplay(PlayState* play, Player* this, s32 lod, Gfx* cullDList,
s32 sp5C;
s32 hoverBootsTimer = this->hoverBootsTimer;
if (this->hoverBootsTimer < 19) {
if (this->hoverBootsTimer < 38) {
if (hoverBootsTimer >= 15) {
D_8085486C = (19 - hoverBootsTimer) * 51.0f;
D_8085486C = 255.0f;
} else if (hoverBootsTimer < 19) {
sp5C = hoverBootsTimer;
@@ -12604,7 +12630,8 @@ s32 func_8084DFF4(PlayState* play, Player* this) {
}
this->unk_84F = 1;
equipItem = giEntry.itemId;
equipNow = CVarGetInteger("gAskToEquip", 0) && equipItem >= ITEM_SWORD_KOKIRI && equipItem <= ITEM_TUNIC_ZORA &&
equipNow = CVarGetInteger("gAskToEquip", 0) && giEntry.modIndex == MOD_NONE &&
equipItem >= ITEM_SWORD_KOKIRI && equipItem <= ITEM_TUNIC_ZORA &&
((gItemAgeReqs[equipItem] == 9 || gItemAgeReqs[equipItem] == gSaveContext.linkAge) ||
CVarGetInteger("gTimelessEquipment", 0));