Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enemy Randomizer V1 #1781

Merged
merged 104 commits into from
Dec 6, 2022
Merged

Enemy Randomizer V1 #1781

merged 104 commits into from
Dec 6, 2022

Conversation

aMannus
Copy link
Contributor

@aMannus aMannus commented Oct 15, 2022

Adds enemy randomizer which currently replaces the enemies that are eligible for randomization to a completely random enemy on every scene load. There's also a "seeded" option that'll keep enemies consistent throughout a randomizer playthrough. Enemies that need more than Deku Nuts and either sticks or a sword are excluded from "clear enemy" rooms to prevent potential softlocks.

Initially I thought we'd have to wait until we thoroughly tested removing object dependency, but right now I'm only removing it while enemy randomizer is enabled, and have a small hack in place to temporarily get rid of some enemies that wouldn't normally spawn. I still think it's good to go back to remove all object dependencies at some stage, but in my opinion that can happen outside of this PR and shouldn't block this either.

This PR also has some fixes for CrowdControl because they were identical for both Enemy Randomizer and Crowd Control, so I didn't feel the need to have a PR afterwards to apply specifically the CC side of things. They are mostly related to "clear enemy" rooms considering more enemies correctly, and fixing like-likes not dropping their equipment because of object dependency.

There's a decent amount of enemies I've had to exclude from randomization for various reasons. Maybe I can go back to some of them and try and find a solution, but some of them really are really difficult to work around. For documentation purposes, here's the enemies currently excluded from randomization:

  • Leevers: Rely on a spawner, otherwise only spawns enemies once and also sometimes crashes the game (not known why).
  • Wolfos in SFM: Gate is opened by killing the Wolfos, can't fall back on checking for no enemies left because of other enemies in SFM. Wolfos actor specifically sets the flag for this gate.
  • First Stalfos in Forest Temple Bow room. Other enemies fall through the hole in the floor.
  • 2 Stalfos in Shadow Temple that attack during the boat ride. Other enemies would spawn in the river on scene load.
  • Nabooru: Would break the cutscene and the door wouldn't open.
  • 2 sets of 2 lizalfos in Doodong's. All 4 spawn when entering the room, then killed off based on your entrance. Can't check for all enemies killed because the 2 faulty ones would be randomized and spawned as well otherwise.
  • One Armos is staying unrandomized in Spirit Temple because it's needed to press down a button.
  • Shell Blades and Spikes in one room in Water Temple are required to be killed to open a gate. Because you're underwater and limited to your hookshot, these aren't randomized.
  • Poes, octoroks and leevers can't be spawned when randomizing an enemy because they require an external objects or a specific surface to function and would die/despawn instantly otherwise.
  • Flare dancers. Technically worked, but often jumps out of bounds and is a potential cause to crashes because it spawns a ton of seperate actors in the form of flames. Not randomizing it in the vanilla locations to have it atleast show up in the game somewhere still.

I'm using this issue to track known issues: #1787

For the future, this concept can definitely be expanded a lot. Before that though, I'd rather get this groundwork working correctly first, but here's some potential ideas:

  • Let the player choose exactly what enemies can be randomized to with a list of checkboxes.
  • Integrate enemy spawns with actual randomizer logic (this would be a huge task).
  • Go back to some of the enemies excluded and potentially find a way around their issues.

Build Artifacts

@aMannus aMannus added the do not merge Not ready or not valid changes label Oct 15, 2022
soh/src/code/z_actor.c Outdated Show resolved Hide resolved
soh/src/code/z_actor.c Outdated Show resolved Hide resolved
@garrettjoecox
Copy link
Contributor

One potential issue I see with this approach is it seems like this doesn't only effect actors spawned from the scene data. My assumption is that if a user was on CC and they spawned any of these enemies it wouldn't spawn what they requested but instead a randomized enemy?

soh/src/code/z_actor.c Outdated Show resolved Hide resolved
@aMannus
Copy link
Contributor Author

aMannus commented Oct 15, 2022

One potential issue I see with this approach is it seems like this doesn't only effect actors spawned from the scene data. My assumption is that if a user was on CC and they spawned any of these enemies it wouldn't spawn what they requested but instead a randomized enemy?

Currently, it would yeah. I can add gMapLoading to the tryRandomizeEnemy check to fix that (haven't tried, but in theory should work), but I'm honestly not sure which of the 2 would be prefered. Currently for example, enemies that spawn enemies (big jellyfish spawn into 3 smaller jellyfish after dying normally) spawn randomized enemies instead of their normal spawns. Another example is Gohma spawning the small eggs, it would just drop eggs instead of randomized enemies.

And now that I think about it, quite a few enemies are spawned by another actor or action, like the graveyard composer poes. So yeah, there would be quite a few differences if we do this only while loading scenes in.

I could definitely also see people having fun with just spawning completely random enemies in CC, but maybe that's more fit to just add as an additional option to CC itself instead.

@briaguya-ai
Copy link
Contributor

I could definitely also see people having fun with just spawning completely random enemies in CC, but maybe that's more fit to just add as an additional option to CC itself instead.

yeah, it should probably be a CC option instead (especially considering the different prices for different enemy spawns stuff)

@briaguya-ai
Copy link
Contributor

Checking for room load seems like it would disable too much. I think the best plan would be to think of scenarios where we specifically don't want to randomize enemies and try to figure out how to know when those happen

@aMannus
Copy link
Contributor Author

aMannus commented Oct 15, 2022

Checking for room load seems like it would disable too much. I think the best plan would be to think of scenarios where we specifically don't want to randomize enemies and try to figure out how to know when those happen

I really wish it was possible to add an argument to C functions with a default value. I'd just add another argument to Actor_Spawn called spawnSource or something, and default it to "regular" or whatever, and make CC use "crowdcontrol" as a value (or well, equivalent enums). But I don't think there's a way to do that right?

Copy link
Contributor

@briaguya-ai briaguya-ai left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall this is looking good to go! I left a few comments (mostly questions) but nothing that would require any huge changes.

This is super exciting! Can't wait to get it merged!

soh/soh/Enhancements/enemyrandomizer.cpp Outdated Show resolved Hide resolved
soh/src/code/z_en_item00.c Outdated Show resolved Hide resolved
soh/src/overlays/actors/ovl_En_Encount1/z_en_encount1.c Outdated Show resolved Hide resolved
soh/src/overlays/actors/ovl_En_Ik/z_en_ik.c Show resolved Hide resolved
soh/src/overlays/actors/ovl_En_Mb/z_en_mb.c Outdated Show resolved Hide resolved
soh/src/overlays/actors/ovl_En_Mb/z_en_mb.c Outdated Show resolved Hide resolved
soh/src/overlays/actors/ovl_En_Mb/z_en_mb.c Outdated Show resolved Hide resolved
soh/src/overlays/actors/ovl_En_Mb/z_en_mb.c Outdated Show resolved Hide resolved
soh/src/overlays/actors/ovl_En_Mb/z_en_mb.c Show resolved Hide resolved
soh/src/overlays/actors/ovl_En_Rd/z_en_rd.c Show resolved Hide resolved
@aMannus
Copy link
Contributor Author

aMannus commented Dec 5, 2022

@briaguya-ai I've clarified the comments in the spots you had questions. Feel free to unresolve any if the new comments aren't clear enough.

Copy link
Contributor

@briaguya-ai briaguya-ai left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is great! Thanks for adding all the clarifying comments @aMannus! Let's :shipit:!

Copy link
Contributor

@dcvz dcvz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 👍 left some very minor comments. I like this setup much better!

There's a few more very specific game modifications that I would hope but they're very cleanly separated, which is nice :)

Comment on lines +3145 to +3153
s16 rotX, s16 rotY, s16 rotZ, s16 params, s16 canRandomize) {

uint8_t tryRandomizeEnemy = CVar_GetS32("gRandomizedEnemies", 0) && gSaveContext.fileNum >= 0 && gSaveContext.fileNum <= 2 && canRandomize;

if (tryRandomizeEnemy) {
if (!GetRandomizedEnemy(play, &actorId, &posX, &posY, &posZ, &rotX, &rotY, &rotZ, &params)) {
return NULL;
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Like this setup much better!

@@ -2506,7 +2507,7 @@ void Actor_UpdateAll(PlayState* play, ActorContext* actorCtx) {
refActor = &GET_PLAYER(play)->actor;
KREG(0) = 0;
Actor_Spawn(&play->actorCtx, play, ACTOR_EN_CLEAR_TAG, refActor->world.pos.x,
refActor->world.pos.y + 100.0f, refActor->world.pos.z, 0, 0, 0, 1);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One small nitpick: sometime's you're passing in 0/1 and sometimes true/false for canRandomize. I'd say let's just stick to one way.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All the .cpp files have to use 1/0 because true/false mean different things in C++ compared to the true/false being a define to 1/0 in C files. I just checked to see and it looks like the one in z_actor.c outlined above is the only one I somehow missed, so I'll change that one real quick.

@dcvz dcvz merged commit 598cac7 into HarbourMasters:develop Dec 6, 2022
@aMannus aMannus deleted the random-enemies branch December 9, 2022 07:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants