mirror of
https://github.com/HarbourMasters/Shipwright.git
synced 2024-11-22 17:32:19 -05:00
RandoV3 fixes for mix/decouple boss entrances
This commit is contained in:
parent
37b960ab0e
commit
8b178f9386
@ -10,6 +10,7 @@ EntranceLinkInfo NO_RETURN_ENTRANCE = { EntranceType::None, RR_NONE, RR_NONE, -1
|
||||
|
||||
Entrance::Entrance(RandomizerRegion connectedRegion_, std::vector<ConditionFn> conditions_met_)
|
||||
: connectedRegion(connectedRegion_) {
|
||||
originalConnectedRegion = connectedRegion_;
|
||||
conditions_met.resize(2);
|
||||
for (size_t i = 0; i < conditions_met_.size(); i++) {
|
||||
conditions_met[i] = conditions_met_[i];
|
||||
@ -109,6 +110,10 @@ RandomizerRegion Entrance::GetConnectedRegionKey() const {
|
||||
return connectedRegion;
|
||||
}
|
||||
|
||||
RandomizerRegion Entrance::GetOriginalConnectedRegionKey() const {
|
||||
return originalConnectedRegion;
|
||||
}
|
||||
|
||||
Area* Entrance::GetConnectedRegion() const {
|
||||
return AreaTable(connectedRegion);
|
||||
}
|
||||
@ -173,14 +178,6 @@ void Entrance::SetIndex(int16_t newIndex) {
|
||||
index = newIndex;
|
||||
}
|
||||
|
||||
int16_t Entrance::GetBlueWarp() const {
|
||||
return blueWarp;
|
||||
}
|
||||
|
||||
void Entrance::SetBlueWarp(int16_t newBlueWarp) {
|
||||
blueWarp = newBlueWarp;
|
||||
}
|
||||
|
||||
Entrance* Entrance::GetAssumed() const {
|
||||
return assumed;
|
||||
}
|
||||
@ -226,7 +223,7 @@ Entrance* Entrance::GetNewTarget() {
|
||||
AreaTable(RR_ROOT)->AddExit(RR_ROOT, connectedRegion, [] { return true; });
|
||||
Entrance* targetEntrance = AreaTable(RR_ROOT)->GetExit(connectedRegion);
|
||||
targetEntrance->SetReplacement(this);
|
||||
targetEntrance->SetName(GetParentRegion()->regionName + " -> " + GetConnectedRegion()->regionName);
|
||||
targetEntrance->SetName(AreaTable(RR_ROOT)->regionName + " -> " + GetConnectedRegion()->regionName);
|
||||
return targetEntrance;
|
||||
}
|
||||
|
||||
@ -246,6 +243,11 @@ void EntranceShuffler::SetNoRandomEntrances(bool noRandomEntrances) {
|
||||
mNoRandomEntrances = noRandomEntrances;
|
||||
}
|
||||
|
||||
// Construct entrance name from parent and connected region keys
|
||||
std::string EntranceNameByRegions(RandomizerRegion parentRegion, RandomizerRegion connectedRegion) {
|
||||
return AreaTable(parentRegion)->regionName + " -> " + AreaTable(connectedRegion)->regionName;
|
||||
}
|
||||
|
||||
void SetAllEntrancesData(std::vector<EntranceInfoPair>& entranceShuffleTable) {
|
||||
auto ctx = Rando::Context::GetInstance();
|
||||
for (auto& entrancePair : entranceShuffleTable) {
|
||||
@ -256,26 +258,22 @@ void SetAllEntrancesData(std::vector<EntranceInfoPair>& entranceShuffleTable) {
|
||||
// set data
|
||||
Entrance* forwardEntrance = AreaTable(forwardEntry.parentRegion)->GetExit(forwardEntry.connectedRegion);
|
||||
forwardEntrance->SetIndex(forwardEntry.index);
|
||||
forwardEntrance->SetBlueWarp(forwardEntry.blueWarp);
|
||||
forwardEntrance->SetType(forwardEntry.type);
|
||||
forwardEntrance->SetAsPrimary();
|
||||
|
||||
// When decouple entrances is on, mark it for entrances except boss rooms
|
||||
if (ctx->GetOption(RSK_DECOUPLED_ENTRANCES) && forwardEntry.type != EntranceType::ChildBoss &&
|
||||
forwardEntry.type != EntranceType::AdultBoss) {
|
||||
// When decouple entrances is on, mark the forward entrance
|
||||
if (ctx->GetOption(RSK_DECOUPLED_ENTRANCES)) {
|
||||
forwardEntrance->SetDecoupled();
|
||||
}
|
||||
|
||||
if (returnEntry.parentRegion != RR_NONE) {
|
||||
Entrance* returnEntrance = AreaTable(returnEntry.parentRegion)->GetExit(returnEntry.connectedRegion);
|
||||
returnEntrance->SetIndex(returnEntry.index);
|
||||
returnEntrance->SetBlueWarp(returnEntry.blueWarp);
|
||||
returnEntrance->SetType(returnEntry.type);
|
||||
forwardEntrance->BindTwoWay(returnEntrance);
|
||||
|
||||
// Mark reverse entrance as decoupled
|
||||
if (ctx->GetOption(RSK_DECOUPLED_ENTRANCES) && returnEntry.type != EntranceType::ChildBoss &&
|
||||
returnEntry.type != EntranceType::AdultBoss) {
|
||||
if (ctx->GetOption(RSK_DECOUPLED_ENTRANCES)) {
|
||||
returnEntrance->SetDecoupled();
|
||||
}
|
||||
}
|
||||
@ -925,24 +923,23 @@ int EntranceShuffler::ShuffleAllEntrances() {
|
||||
mCurNumRandomizedEntrances = 0;
|
||||
|
||||
std::vector<EntranceInfoPair> entranceShuffleTable = {
|
||||
// Parent Region Connected Region index blue warp
|
||||
// Type Parent Region Connected Region Index
|
||||
{ { EntranceType::Dungeon, RR_KF_OUTSIDE_DEKU_TREE, RR_DEKU_TREE_ENTRYWAY, 0x0000 },
|
||||
{ EntranceType::Dungeon, RR_DEKU_TREE_ENTRYWAY, RR_KF_OUTSIDE_DEKU_TREE, 0x0209, 0x0457 } },
|
||||
{ EntranceType::Dungeon, RR_DEKU_TREE_ENTRYWAY, RR_KF_OUTSIDE_DEKU_TREE, 0x0209 } },
|
||||
{ { EntranceType::Dungeon, RR_DEATH_MOUNTAIN_TRAIL, RR_DODONGOS_CAVERN_ENTRYWAY, 0x0004 },
|
||||
{ EntranceType::Dungeon, RR_DODONGOS_CAVERN_ENTRYWAY, RR_DEATH_MOUNTAIN_TRAIL, 0x0242, 0x047A } },
|
||||
{ EntranceType::Dungeon, RR_DODONGOS_CAVERN_ENTRYWAY, RR_DEATH_MOUNTAIN_TRAIL, 0x0242 } },
|
||||
{ { EntranceType::Dungeon, RR_ZORAS_FOUNTAIN, RR_JABU_JABUS_BELLY_ENTRYWAY, 0x0028 },
|
||||
{ EntranceType::Dungeon, RR_JABU_JABUS_BELLY_ENTRYWAY, RR_ZORAS_FOUNTAIN, 0x0221, 0x010E } },
|
||||
{ EntranceType::Dungeon, RR_JABU_JABUS_BELLY_ENTRYWAY, RR_ZORAS_FOUNTAIN, 0x0221 } },
|
||||
{ { EntranceType::Dungeon, RR_SACRED_FOREST_MEADOW, RR_FOREST_TEMPLE_ENTRYWAY, 0x0169 },
|
||||
{ EntranceType::Dungeon, RR_FOREST_TEMPLE_ENTRYWAY, RR_SACRED_FOREST_MEADOW, 0x0215, 0x0608 } },
|
||||
{ EntranceType::Dungeon, RR_FOREST_TEMPLE_ENTRYWAY, RR_SACRED_FOREST_MEADOW, 0x0215 } },
|
||||
{ { EntranceType::Dungeon, RR_DMC_CENTRAL_LOCAL, RR_FIRE_TEMPLE_ENTRYWAY, 0x0165 },
|
||||
{ EntranceType::Dungeon, RR_FIRE_TEMPLE_ENTRYWAY, RR_DMC_CENTRAL_LOCAL, 0x024A, 0x0564 } },
|
||||
{ EntranceType::Dungeon, RR_FIRE_TEMPLE_ENTRYWAY, RR_DMC_CENTRAL_LOCAL, 0x024A } },
|
||||
{ { EntranceType::Dungeon, RR_LAKE_HYLIA, RR_WATER_TEMPLE_ENTRYWAY, 0x0010 },
|
||||
{ EntranceType::Dungeon, RR_WATER_TEMPLE_ENTRYWAY, RR_LAKE_HYLIA, 0x021D, 0x060C } },
|
||||
{ EntranceType::Dungeon, RR_WATER_TEMPLE_ENTRYWAY, RR_LAKE_HYLIA, 0x021D } },
|
||||
{ { EntranceType::Dungeon, RR_DESERT_COLOSSUS, RR_SPIRIT_TEMPLE_ENTRYWAY, 0x0082 },
|
||||
{ EntranceType::Dungeon, RR_SPIRIT_TEMPLE_ENTRYWAY, RR_DESERT_COLOSSUS_FROM_SPIRIT_ENTRYWAY, 0x01E1,
|
||||
0x0610 } },
|
||||
{ EntranceType::Dungeon, RR_SPIRIT_TEMPLE_ENTRYWAY, RR_DESERT_COLOSSUS_FROM_SPIRIT_ENTRYWAY, 0x01E1 } },
|
||||
{ { EntranceType::Dungeon, RR_GRAVEYARD_WARP_PAD_REGION, RR_SHADOW_TEMPLE_ENTRYWAY, 0x0037 },
|
||||
{ EntranceType::Dungeon, RR_SHADOW_TEMPLE_ENTRYWAY, RR_GRAVEYARD_WARP_PAD_REGION, 0x0205, 0x0580 } },
|
||||
{ EntranceType::Dungeon, RR_SHADOW_TEMPLE_ENTRYWAY, RR_GRAVEYARD_WARP_PAD_REGION, 0x0205 } },
|
||||
{ { EntranceType::Dungeon, RR_KAKARIKO_VILLAGE, RR_BOTTOM_OF_THE_WELL_ENTRYWAY, 0x0098 },
|
||||
{ EntranceType::Dungeon, RR_BOTTOM_OF_THE_WELL_ENTRYWAY, RR_KAKARIKO_VILLAGE, 0x02A6 } },
|
||||
{ { EntranceType::Dungeon, RR_ZORAS_FOUNTAIN, RR_ICE_CAVERN_ENTRYWAY, 0x0088 },
|
||||
@ -1190,22 +1187,32 @@ int EntranceShuffler::ShuffleAllEntrances() {
|
||||
{ { EntranceType::WarpSong, RR_PRELUDE_OF_LIGHT_WARP, RR_TEMPLE_OF_TIME, 0x05F4 }, NO_RETURN_ENTRANCE },
|
||||
|
||||
{ { EntranceType::ChildBoss, RR_DEKU_TREE_BOSS_ENTRYWAY, RR_DEKU_TREE_BOSS_ROOM, 0x040F },
|
||||
{ EntranceType::ChildBoss, RR_DEKU_TREE_BOSS_ROOM, RR_DEKU_TREE_BOSS_ENTRYWAY, 0x0252, 0x0457 } },
|
||||
{ EntranceType::ChildBoss, RR_DEKU_TREE_BOSS_ROOM, RR_DEKU_TREE_BOSS_ENTRYWAY, 0x0252 } },
|
||||
{ { EntranceType::ChildBoss, RR_DODONGOS_CAVERN_BOSS_ENTRYWAY, RR_DODONGOS_CAVERN_BOSS_ROOM, 0x040B },
|
||||
{ EntranceType::ChildBoss, RR_DODONGOS_CAVERN_BOSS_ROOM, RR_DODONGOS_CAVERN_BOSS_ENTRYWAY, 0x00C5, 0x047A } },
|
||||
{ EntranceType::ChildBoss, RR_DODONGOS_CAVERN_BOSS_ROOM, RR_DODONGOS_CAVERN_BOSS_ENTRYWAY, 0x00C5 } },
|
||||
{ { EntranceType::ChildBoss, RR_JABU_JABUS_BELLY_BOSS_ENTRYWAY, RR_JABU_JABUS_BELLY_BOSS_ROOM, 0x0301 },
|
||||
{ EntranceType::ChildBoss, RR_JABU_JABUS_BELLY_BOSS_ROOM, RR_JABU_JABUS_BELLY_BOSS_ENTRYWAY, 0x0407,
|
||||
0x010E } },
|
||||
{ EntranceType::ChildBoss, RR_JABU_JABUS_BELLY_BOSS_ROOM, RR_JABU_JABUS_BELLY_BOSS_ENTRYWAY, 0x0407 } },
|
||||
{ { EntranceType::AdultBoss, RR_FOREST_TEMPLE_BOSS_ENTRYWAY, RR_FOREST_TEMPLE_BOSS_ROOM, 0x000C },
|
||||
{ EntranceType::AdultBoss, RR_FOREST_TEMPLE_BOSS_ROOM, RR_FOREST_TEMPLE_BOSS_ENTRYWAY, 0x024E, 0x0608 } },
|
||||
{ EntranceType::AdultBoss, RR_FOREST_TEMPLE_BOSS_ROOM, RR_FOREST_TEMPLE_BOSS_ENTRYWAY, 0x024E } },
|
||||
{ { EntranceType::AdultBoss, RR_FIRE_TEMPLE_BOSS_ENTRYWAY, RR_FIRE_TEMPLE_BOSS_ROOM, 0x0305 },
|
||||
{ EntranceType::AdultBoss, RR_FIRE_TEMPLE_BOSS_ROOM, RR_FIRE_TEMPLE_BOSS_ENTRYWAY, 0x0175, 0x0564 } },
|
||||
{ EntranceType::AdultBoss, RR_FIRE_TEMPLE_BOSS_ROOM, RR_FIRE_TEMPLE_BOSS_ENTRYWAY, 0x0175 } },
|
||||
{ { EntranceType::AdultBoss, RR_WATER_TEMPLE_BOSS_ENTRYWAY, RR_WATER_TEMPLE_BOSS_ROOM, 0x0417 },
|
||||
{ EntranceType::AdultBoss, RR_WATER_TEMPLE_BOSS_ROOM, RR_WATER_TEMPLE_BOSS_ENTRYWAY, 0x0423, 0x060C } },
|
||||
{ EntranceType::AdultBoss, RR_WATER_TEMPLE_BOSS_ROOM, RR_WATER_TEMPLE_BOSS_ENTRYWAY, 0x0423 } },
|
||||
{ { EntranceType::AdultBoss, RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, RR_SPIRIT_TEMPLE_BOSS_ROOM, 0x008D },
|
||||
{ EntranceType::AdultBoss, RR_SPIRIT_TEMPLE_BOSS_ROOM, RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, 0x02F5, 0x0610 } },
|
||||
{ EntranceType::AdultBoss, RR_SPIRIT_TEMPLE_BOSS_ROOM, RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, 0x02F5 } },
|
||||
{ { EntranceType::AdultBoss, RR_SHADOW_TEMPLE_BOSS_ENTRYWAY, RR_SHADOW_TEMPLE_BOSS_ROOM, 0x0413 },
|
||||
{ EntranceType::AdultBoss, RR_SHADOW_TEMPLE_BOSS_ROOM, RR_SHADOW_TEMPLE_BOSS_ENTRYWAY, 0x02B2, 0x0580 } },
|
||||
{ EntranceType::AdultBoss, RR_SHADOW_TEMPLE_BOSS_ROOM, RR_SHADOW_TEMPLE_BOSS_ENTRYWAY, 0x02B2 } },
|
||||
|
||||
{ { EntranceType::BlueWarp, RR_DEKU_TREE_BOSS_ROOM, RR_KF_OUTSIDE_DEKU_TREE, 0x0457 }, NO_RETURN_ENTRANCE },
|
||||
{ { EntranceType::BlueWarp, RR_DODONGOS_CAVERN_BOSS_ROOM, RR_DEATH_MOUNTAIN_TRAIL, 0x047A },
|
||||
NO_RETURN_ENTRANCE },
|
||||
{ { EntranceType::BlueWarp, RR_JABU_JABUS_BELLY_BOSS_ROOM, RR_ZORAS_FOUNTAIN, 0x010E }, NO_RETURN_ENTRANCE },
|
||||
{ { EntranceType::BlueWarp, RR_FOREST_TEMPLE_BOSS_ROOM, RR_SACRED_FOREST_MEADOW, 0x0608 }, NO_RETURN_ENTRANCE },
|
||||
{ { EntranceType::BlueWarp, RR_FIRE_TEMPLE_BOSS_ROOM, RR_DMC_CENTRAL_LOCAL, 0x0564 }, NO_RETURN_ENTRANCE },
|
||||
{ { EntranceType::BlueWarp, RR_WATER_TEMPLE_BOSS_ROOM, RR_LAKE_HYLIA, 0x060C }, NO_RETURN_ENTRANCE },
|
||||
{ { EntranceType::BlueWarp, RR_SPIRIT_TEMPLE_BOSS_ROOM, RR_DESERT_COLOSSUS, 0x0610 }, NO_RETURN_ENTRANCE },
|
||||
{ { EntranceType::BlueWarp, RR_SHADOW_TEMPLE_BOSS_ROOM, RR_GRAVEYARD_WARP_PAD_REGION, 0x0580 },
|
||||
NO_RETURN_ENTRANCE },
|
||||
};
|
||||
|
||||
std::map<std::string, PriorityEntrance> priorityEntranceTable = {
|
||||
@ -1262,6 +1269,11 @@ int EntranceShuffler::ShuffleAllEntrances() {
|
||||
entrance->GetConnectedRegionKey() == RR_DEKU_TREE_BOSS_ROOM;
|
||||
});
|
||||
}
|
||||
if (ctx->GetOption(RSK_DECOUPLED_ENTRANCES)) {
|
||||
for (Entrance* entrance : entrancePools[EntranceType::Boss]) {
|
||||
entrancePools[EntranceType::BossReverse].push_back(entrance->GetReverse());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
entrancePools[EntranceType::ChildBoss] = GetShuffleableEntrances(EntranceType::ChildBoss);
|
||||
entrancePools[EntranceType::AdultBoss] = GetShuffleableEntrances(EntranceType::AdultBoss);
|
||||
@ -1273,6 +1285,14 @@ int EntranceShuffler::ShuffleAllEntrances() {
|
||||
entrance->GetConnectedRegionKey() == RR_DEKU_TREE_BOSS_ROOM;
|
||||
});
|
||||
}
|
||||
if (ctx->GetOption(RSK_DECOUPLED_ENTRANCES)) {
|
||||
for (Entrance* entrance : entrancePools[EntranceType::ChildBoss]) {
|
||||
entrancePools[EntranceType::ChildBossReverse].push_back(entrance->GetReverse());
|
||||
}
|
||||
for (Entrance* entrance : entrancePools[EntranceType::AdultBoss]) {
|
||||
entrancePools[EntranceType::AdultBossReverse].push_back(entrance->GetReverse());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1346,11 +1366,13 @@ int EntranceShuffler::ShuffleAllEntrances() {
|
||||
|
||||
// combine entrance pools if mixing pools. Only continue if more than one pool is selected.
|
||||
int totalMixedPools =
|
||||
(ctx->GetOption(RSK_MIX_DUNGEON_ENTRANCES) ? 1 : 0) + (ctx->GetOption(RSK_MIX_OVERWORLD_ENTRANCES) ? 1 : 0) +
|
||||
(ctx->GetOption(RSK_MIX_INTERIOR_ENTRANCES) ? 1 : 0) + (ctx->GetOption(RSK_MIX_GROTTO_ENTRANCES) ? 1 : 0);
|
||||
(ctx->GetOption(RSK_MIX_DUNGEON_ENTRANCES) ? 1 : 0) + (ctx->GetOption(RSK_MIX_BOSS_ENTRANCES) ? 1 : 0) +
|
||||
(ctx->GetOption(RSK_MIX_OVERWORLD_ENTRANCES) ? 1 : 0) + (ctx->GetOption(RSK_MIX_INTERIOR_ENTRANCES) ? 1 : 0) +
|
||||
(ctx->GetOption(RSK_MIX_GROTTO_ENTRANCES) ? 1 : 0);
|
||||
if (totalMixedPools < 2) {
|
||||
ctx->GetOption(RSK_MIXED_ENTRANCE_POOLS).SetSelectedIndex(RO_GENERIC_OFF);
|
||||
ctx->GetOption(RSK_MIX_DUNGEON_ENTRANCES).SetSelectedIndex(RO_GENERIC_OFF);
|
||||
ctx->GetOption(RSK_MIX_BOSS_ENTRANCES).SetSelectedIndex(RO_GENERIC_OFF);
|
||||
ctx->GetOption(RSK_MIX_OVERWORLD_ENTRANCES).SetSelectedIndex(RO_GENERIC_OFF);
|
||||
ctx->GetOption(RSK_MIX_INTERIOR_ENTRANCES).SetSelectedIndex(RO_GENERIC_OFF);
|
||||
ctx->GetOption(RSK_MIX_GROTTO_ENTRANCES).SetSelectedIndex(RO_GENERIC_OFF);
|
||||
@ -1364,6 +1386,12 @@ int EntranceShuffler::ShuffleAllEntrances() {
|
||||
poolsToMix.insert(EntranceType::DungeonReverse);
|
||||
}
|
||||
}
|
||||
if (ctx->GetOption(RSK_MIX_DUNGEON_ENTRANCES)) {
|
||||
poolsToMix.insert(EntranceType::Boss);
|
||||
if (ctx->GetOption(RSK_DECOUPLED_ENTRANCES)) {
|
||||
poolsToMix.insert(EntranceType::BossReverse);
|
||||
}
|
||||
}
|
||||
if (ctx->GetOption(RSK_SHUFFLE_OVERWORLD_ENTRANCES)) {
|
||||
poolsToMix.insert(EntranceType::Overworld);
|
||||
}
|
||||
@ -1503,6 +1531,94 @@ int EntranceShuffler::ShuffleAllEntrances() {
|
||||
}
|
||||
}
|
||||
|
||||
// Determine blue warp targets
|
||||
// RANDOTODO: add bluewarp shuffle
|
||||
if (true /* ctx->GetOption(RSK_SHUFFLE_BLUEWARP_ENTRANCES).Is(RO_BLUEWARP_ENTRANCE_SHUFFLE_DUNGEON) */) {
|
||||
// If a boss room is inside a boss door, make the blue warp go outside the dungeon's entrance
|
||||
std::map<std::string, Entrance*> bossExits = {
|
||||
{ EntranceNameByRegions(RR_DEKU_TREE_BOSS_ROOM, RR_DEKU_TREE_BOSS_ENTRYWAY),
|
||||
GetEntrance(EntranceNameByRegions(RR_DEKU_TREE_ENTRYWAY, RR_KF_OUTSIDE_DEKU_TREE)) },
|
||||
{ EntranceNameByRegions(RR_DODONGOS_CAVERN_BOSS_ROOM, RR_DODONGOS_CAVERN_BOSS_ENTRYWAY),
|
||||
GetEntrance(EntranceNameByRegions(RR_DODONGOS_CAVERN_ENTRYWAY, RR_DEATH_MOUNTAIN_TRAIL)) },
|
||||
{ EntranceNameByRegions(RR_JABU_JABUS_BELLY_BOSS_ROOM, RR_JABU_JABUS_BELLY_BOSS_ENTRYWAY),
|
||||
GetEntrance(EntranceNameByRegions(RR_JABU_JABUS_BELLY_ENTRYWAY, RR_ZORAS_FOUNTAIN)) },
|
||||
{ EntranceNameByRegions(RR_FOREST_TEMPLE_BOSS_ROOM, RR_FOREST_TEMPLE_BOSS_ENTRYWAY),
|
||||
GetEntrance(EntranceNameByRegions(RR_FOREST_TEMPLE_ENTRYWAY, RR_SACRED_FOREST_MEADOW)) },
|
||||
{ EntranceNameByRegions(RR_FIRE_TEMPLE_BOSS_ROOM, RR_FIRE_TEMPLE_BOSS_ENTRYWAY),
|
||||
GetEntrance(EntranceNameByRegions(RR_FIRE_TEMPLE_ENTRYWAY, RR_DMC_CENTRAL_LOCAL)) },
|
||||
{ EntranceNameByRegions(RR_WATER_TEMPLE_BOSS_ROOM, RR_WATER_TEMPLE_BOSS_ENTRYWAY),
|
||||
GetEntrance(EntranceNameByRegions(RR_WATER_TEMPLE_ENTRYWAY, RR_LAKE_HYLIA)) },
|
||||
{ EntranceNameByRegions(RR_SPIRIT_TEMPLE_BOSS_ROOM, RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY),
|
||||
GetEntrance(EntranceNameByRegions(RR_SPIRIT_TEMPLE_ENTRYWAY, RR_DESERT_COLOSSUS_FROM_SPIRIT_ENTRYWAY)) },
|
||||
{ EntranceNameByRegions(RR_SHADOW_TEMPLE_BOSS_ROOM, RR_SHADOW_TEMPLE_BOSS_ENTRYWAY),
|
||||
GetEntrance(EntranceNameByRegions(RR_SHADOW_TEMPLE_ENTRYWAY, RR_GRAVEYARD_WARP_PAD_REGION)) },
|
||||
};
|
||||
|
||||
// If a boss room is inside a dungeon entrance (or inside a dungeon which is inside a dungeon entrance), make
|
||||
// the blue warp go to that dungeon's blue warp target
|
||||
std::map<std::string, Entrance*> dungeonExits = {
|
||||
{ EntranceNameByRegions(RR_DEKU_TREE_ENTRYWAY, RR_KF_OUTSIDE_DEKU_TREE),
|
||||
GetEntrance(EntranceNameByRegions(RR_DEKU_TREE_BOSS_ROOM, RR_KF_OUTSIDE_DEKU_TREE)) },
|
||||
{ EntranceNameByRegions(RR_DODONGOS_CAVERN_ENTRYWAY, RR_DEATH_MOUNTAIN_TRAIL),
|
||||
GetEntrance(EntranceNameByRegions(RR_DODONGOS_CAVERN_BOSS_ROOM, RR_DEATH_MOUNTAIN_TRAIL)) },
|
||||
{ EntranceNameByRegions(RR_JABU_JABUS_BELLY_ENTRYWAY, RR_ZORAS_FOUNTAIN),
|
||||
GetEntrance(EntranceNameByRegions(RR_JABU_JABUS_BELLY_BOSS_ROOM, RR_ZORAS_FOUNTAIN)) },
|
||||
{ EntranceNameByRegions(RR_FOREST_TEMPLE_ENTRYWAY, RR_SACRED_FOREST_MEADOW),
|
||||
GetEntrance(EntranceNameByRegions(RR_FOREST_TEMPLE_BOSS_ROOM, RR_SACRED_FOREST_MEADOW)) },
|
||||
{ EntranceNameByRegions(RR_FIRE_TEMPLE_ENTRYWAY, RR_DMC_CENTRAL_LOCAL),
|
||||
GetEntrance(EntranceNameByRegions(RR_FIRE_TEMPLE_BOSS_ROOM, RR_DMC_CENTRAL_LOCAL)) },
|
||||
{ EntranceNameByRegions(RR_WATER_TEMPLE_ENTRYWAY, RR_LAKE_HYLIA),
|
||||
GetEntrance(EntranceNameByRegions(RR_WATER_TEMPLE_BOSS_ROOM, RR_LAKE_HYLIA)) },
|
||||
{ EntranceNameByRegions(RR_SPIRIT_TEMPLE_ENTRYWAY, RR_DESERT_COLOSSUS_FROM_SPIRIT_ENTRYWAY),
|
||||
GetEntrance(EntranceNameByRegions(RR_SPIRIT_TEMPLE_BOSS_ROOM, RR_DESERT_COLOSSUS)) },
|
||||
{ EntranceNameByRegions(RR_SHADOW_TEMPLE_ENTRYWAY, RR_GRAVEYARD_WARP_PAD_REGION),
|
||||
GetEntrance(EntranceNameByRegions(RR_SHADOW_TEMPLE_BOSS_ROOM, RR_GRAVEYARD_WARP_PAD_REGION)) },
|
||||
};
|
||||
|
||||
// Pair <BlueWarp exit, BossRoom reverse exit>
|
||||
std::vector<EntrancePair> bossRoomExitPairs = {
|
||||
{ GetEntrance(EntranceNameByRegions(RR_DEKU_TREE_BOSS_ROOM, RR_KF_OUTSIDE_DEKU_TREE)),
|
||||
GetEntrance(EntranceNameByRegions(RR_DEKU_TREE_BOSS_ROOM, RR_DEKU_TREE_BOSS_ENTRYWAY)) },
|
||||
{ GetEntrance(EntranceNameByRegions(RR_DODONGOS_CAVERN_BOSS_ROOM, RR_DEATH_MOUNTAIN_TRAIL)),
|
||||
GetEntrance(EntranceNameByRegions(RR_DODONGOS_CAVERN_BOSS_ROOM, RR_DODONGOS_CAVERN_BOSS_ENTRYWAY)) },
|
||||
{ GetEntrance(EntranceNameByRegions(RR_JABU_JABUS_BELLY_BOSS_ROOM, RR_ZORAS_FOUNTAIN)),
|
||||
GetEntrance(EntranceNameByRegions(RR_JABU_JABUS_BELLY_BOSS_ROOM, RR_JABU_JABUS_BELLY_BOSS_ENTRYWAY)) },
|
||||
{ GetEntrance(EntranceNameByRegions(RR_FOREST_TEMPLE_BOSS_ROOM, RR_SACRED_FOREST_MEADOW)),
|
||||
GetEntrance(EntranceNameByRegions(RR_FOREST_TEMPLE_BOSS_ROOM, RR_FOREST_TEMPLE_BOSS_ENTRYWAY)) },
|
||||
{ GetEntrance(EntranceNameByRegions(RR_FIRE_TEMPLE_BOSS_ROOM, RR_DMC_CENTRAL_LOCAL)),
|
||||
GetEntrance(EntranceNameByRegions(RR_FIRE_TEMPLE_BOSS_ROOM, RR_FIRE_TEMPLE_BOSS_ENTRYWAY)) },
|
||||
{ GetEntrance(EntranceNameByRegions(RR_WATER_TEMPLE_BOSS_ROOM, RR_LAKE_HYLIA)),
|
||||
GetEntrance(EntranceNameByRegions(RR_WATER_TEMPLE_BOSS_ROOM, RR_WATER_TEMPLE_BOSS_ENTRYWAY)) },
|
||||
{ GetEntrance(EntranceNameByRegions(RR_SPIRIT_TEMPLE_BOSS_ROOM, RR_DESERT_COLOSSUS)),
|
||||
GetEntrance(EntranceNameByRegions(RR_SPIRIT_TEMPLE_BOSS_ROOM, RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY)) },
|
||||
{ GetEntrance(EntranceNameByRegions(RR_SHADOW_TEMPLE_BOSS_ROOM, RR_GRAVEYARD_WARP_PAD_REGION)),
|
||||
GetEntrance(EntranceNameByRegions(RR_SHADOW_TEMPLE_BOSS_ROOM, RR_SHADOW_TEMPLE_BOSS_ENTRYWAY)) },
|
||||
};
|
||||
|
||||
for (EntrancePair pair : bossRoomExitPairs) {
|
||||
Entrance* target = pair.second->GetReplacement() != nullptr ? pair.second->GetReplacement() : pair.second;
|
||||
|
||||
if (!ctx->GetOption(RSK_DECOUPLED_ENTRANCES)) {
|
||||
while (bossExits.find(target->GetName()) != bossExits.end()) {
|
||||
Entrance* next = bossExits.at(target->GetName());
|
||||
target = next->GetReplacement() != nullptr ? next->GetReplacement() : next;
|
||||
}
|
||||
|
||||
if (dungeonExits.find(target->GetName()) != dungeonExits.end()) {
|
||||
target = dungeonExits.at(target->GetName());
|
||||
}
|
||||
}
|
||||
|
||||
pair.first->Connect(target->GetOriginalConnectedRegionKey());
|
||||
pair.first->SetReplacement(target);
|
||||
}
|
||||
}
|
||||
|
||||
// Validate the world one last time to ensure all special conditions are still valid
|
||||
if (!ValidateWorld(nullptr)) {
|
||||
return ENTRANCE_SHUFFLE_FAILURE;
|
||||
}
|
||||
|
||||
return ENTRANCE_SHUFFLE_SUCCESS;
|
||||
}
|
||||
|
||||
@ -1518,16 +1634,21 @@ void EntranceShuffler::CreateEntranceOverrides() {
|
||||
int i = 0;
|
||||
for (Entrance* entrance : allShuffleableEntrances) {
|
||||
|
||||
// Include blue warps when dungeons or bosses are shuffled
|
||||
bool includeBluewarps =
|
||||
entrance->GetType() == Rando::EntranceType::BlueWarp &&
|
||||
(ctx->GetOption(RSK_SHUFFLE_DUNGEON_ENTRANCES) || ctx->GetOption(RSK_SHUFFLE_BOSS_ENTRANCES));
|
||||
|
||||
// Double-check to make sure the entrance is actually shuffled
|
||||
if (!entrance->IsShuffled()) {
|
||||
if (!entrance->IsShuffled() && !includeBluewarps) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto message = "Setting " + entrance->to_string() + "\n";
|
||||
SPDLOG_DEBUG(message);
|
||||
|
||||
uint8_t type = (uint8_t)entrance->GetType();
|
||||
int16_t originalIndex = entrance->GetIndex();
|
||||
int16_t originalBlueWarp = entrance->GetBlueWarp();
|
||||
int16_t replacementIndex = entrance->GetReplacement()->GetIndex();
|
||||
|
||||
int16_t destinationIndex = -1;
|
||||
@ -1541,9 +1662,9 @@ void EntranceShuffler::CreateEntranceOverrides() {
|
||||
}
|
||||
|
||||
entranceOverrides[i] = {
|
||||
.type = type,
|
||||
.index = originalIndex,
|
||||
.destination = destinationIndex,
|
||||
.blueWarp = originalBlueWarp,
|
||||
.override = replacementIndex,
|
||||
.overrideDestination = replacementDestinationIndex,
|
||||
};
|
||||
@ -1559,9 +1680,9 @@ void EntranceShuffler::CreateEntranceOverrides() {
|
||||
/// @brief set all the entrances to be 0 to indicate an unshuffled entrance
|
||||
void EntranceShuffler::UnshuffleAllEntrances() {
|
||||
for (auto& entranceOveride : entranceOverrides) {
|
||||
entranceOveride.type = 0;
|
||||
entranceOveride.index = 0;
|
||||
entranceOveride.destination = 0;
|
||||
entranceOveride.blueWarp = 0;
|
||||
entranceOveride.override = 0;
|
||||
entranceOveride.overrideDestination = 0;
|
||||
}
|
||||
@ -1575,12 +1696,12 @@ void EntranceShuffler::ParseJson(nlohmann::json spoilerFileJson) {
|
||||
for (auto it = entrancesJson.begin(); it != entrancesJson.end(); ++it, i++) {
|
||||
nlohmann::json entranceJson = *it;
|
||||
for (auto entranceIt = entranceJson.begin(); entranceIt != entranceJson.end(); ++entranceIt) {
|
||||
if (entranceIt.key() == "index") {
|
||||
if (entranceIt.key() == "type") {
|
||||
entranceOverrides[i].type = entranceIt.value();
|
||||
} else if (entranceIt.key() == "index") {
|
||||
entranceOverrides[i].index = entranceIt.value();
|
||||
} else if (entranceIt.key() == "destination") {
|
||||
entranceOverrides[i].destination = entranceIt.value();
|
||||
} else if (entranceIt.key() == "blueWarp") {
|
||||
entranceOverrides[i].blueWarp = entranceIt.value();
|
||||
} else if (entranceIt.key() == "override") {
|
||||
entranceOverrides[i].override = entranceIt.value();
|
||||
} else if (entranceIt.key() == "overrideDestination") {
|
||||
|
@ -15,12 +15,16 @@ enum class EntranceType {
|
||||
OwlDrop,
|
||||
Spawn,
|
||||
WarpSong,
|
||||
BlueWarp,
|
||||
Dungeon,
|
||||
GanonDungeon,
|
||||
DungeonReverse,
|
||||
Boss,
|
||||
BossReverse,
|
||||
ChildBoss,
|
||||
ChildBossReverse,
|
||||
AdultBoss,
|
||||
AdultBossReverse,
|
||||
Interior,
|
||||
InteriorReverse,
|
||||
SpecialInterior,
|
||||
@ -45,6 +49,7 @@ class Entrance {
|
||||
uint32_t Getuint32_t() const;
|
||||
bool CheckConditionAtAgeTime(bool& age, bool& time, bool passAnyway = false) const;
|
||||
RandomizerRegion GetConnectedRegionKey() const;
|
||||
RandomizerRegion GetOriginalConnectedRegionKey() const;
|
||||
Area* GetConnectedRegion() const;
|
||||
void SetParentRegion(RandomizerRegion newParent);
|
||||
RandomizerRegion GetParentRegionKey() const;
|
||||
@ -61,8 +66,6 @@ class Entrance {
|
||||
void SetDecoupled();
|
||||
int16_t GetIndex() const;
|
||||
void SetIndex(int16_t newIndex);
|
||||
int16_t GetBlueWarp() const;
|
||||
void SetBlueWarp(int16_t newBlueWarp);
|
||||
Entrance* GetAssumed() const;
|
||||
void SetReplacement(Entrance* newReplacement);
|
||||
Entrance* GetReplacement() const;
|
||||
@ -78,6 +81,7 @@ class Entrance {
|
||||
private:
|
||||
RandomizerRegion parentRegion;
|
||||
RandomizerRegion connectedRegion;
|
||||
RandomizerRegion originalConnectedRegion;
|
||||
std::vector<ConditionFn> conditions_met;
|
||||
|
||||
EntranceType type = EntranceType::None;
|
||||
@ -86,7 +90,6 @@ class Entrance {
|
||||
Entrance* assumed = nullptr;
|
||||
Entrance* replacement = nullptr;
|
||||
int16_t index = 0xFFFF;
|
||||
int16_t blueWarp = 0;
|
||||
bool shuffled = false;
|
||||
bool primary = false;
|
||||
bool addedToPool = false;
|
||||
@ -99,7 +102,6 @@ typedef struct {
|
||||
RandomizerRegion parentRegion;
|
||||
RandomizerRegion connectedRegion;
|
||||
int16_t index;
|
||||
int16_t blueWarp;
|
||||
} EntranceLinkInfo;
|
||||
|
||||
typedef struct {
|
||||
|
@ -170,12 +170,14 @@ void Settings::CreateOptionDescriptions() {
|
||||
"This also adds the one-way entrance from Gerudo Valley to Lake Hylia in the pool of "
|
||||
"overworld entrances when they are shuffled.";
|
||||
mOptionDescriptions[RSK_MIXED_ENTRANCE_POOLS] =
|
||||
"Shuffle entrances into a mixed pool instead of separate ones.\n"
|
||||
"Shuffle entrances into a mixed pool instead of separate ones. Has no affect on pools whose "
|
||||
"entrances aren't shuffled, and \"Shuffle Boss Entrances\" must be set to \"Full\" to include them.\n"
|
||||
"\n"
|
||||
"For example, enabling the settings to shuffle grotto, dungeon, and overworld entrances and "
|
||||
"selecting grotto and dungeon entrances here will allow a dungeon to be inside a grotto or "
|
||||
"vice versa, while overworld entrances are shuffled in their own separate pool and indoors stay vanilla.";
|
||||
mOptionDescriptions[RSK_MIX_DUNGEON_ENTRANCES] = "Dungeon entrances will be part of the mixed pool";
|
||||
mOptionDescriptions[RSK_MIX_BOSS_ENTRANCES] = "Boss entrances will be part of the mixed pool";
|
||||
mOptionDescriptions[RSK_MIX_OVERWORLD_ENTRANCES] = "Overworld entrances will be part of the mixed pool";
|
||||
mOptionDescriptions[RSK_MIX_INTERIOR_ENTRANCES] = "Interior entrances will be part of the mixed pool";
|
||||
mOptionDescriptions[RSK_MIX_GROTTO_ENTRANCES] = "Grotto entrances will be part of the mixed pool";
|
||||
|
@ -236,23 +236,6 @@ std::unordered_map<s16, s16> getItemIdToItemId = {
|
||||
{ GI_CLAIM_CHECK, ITEM_CLAIM_CHECK }
|
||||
};
|
||||
|
||||
std::string sanitize(std::string stringValue) {
|
||||
// Add backslashes.
|
||||
for (auto i = stringValue.begin();;) {
|
||||
auto const pos = std::find_if(i, stringValue.end(), [](char const c) { return '\\' == c || '\'' == c || '"' == c; });
|
||||
if (pos == stringValue.end()) {
|
||||
break;
|
||||
}
|
||||
i = std::next(stringValue.insert(pos, '\\'), 2);
|
||||
}
|
||||
|
||||
// Removes others.
|
||||
stringValue.erase(std::remove_if(stringValue.begin(), stringValue.end(), [](char const c) {
|
||||
return '\n' == c || '\r' == c || '\0' == c || '\x1A' == c; }), stringValue.end());
|
||||
|
||||
return stringValue;
|
||||
}
|
||||
|
||||
#pragma optimize("", off)
|
||||
#pragma GCC push_options
|
||||
#pragma GCC optimize ("O0")
|
||||
|
@ -68,6 +68,7 @@ void Settings::CreateOptions() {
|
||||
mOptions[RSK_SHUFFLE_OVERWORLD_SPAWNS] = Option::Bool("Overworld Spawns", "gRandomizeShuffleOverworldSpanws", mOptionDescriptions[RSK_SHUFFLE_OVERWORLD_SPAWNS]);
|
||||
mOptions[RSK_MIXED_ENTRANCE_POOLS] = Option::Bool("Mixed Entrance Pools", "gRandomizeMixedEntrances", mOptionDescriptions[RSK_MIXED_ENTRANCE_POOLS]);
|
||||
mOptions[RSK_MIX_DUNGEON_ENTRANCES] = Option::Bool("Mix Dungeons", "gRandomizeMixDungeons", mOptionDescriptions[RSK_MIX_DUNGEON_ENTRANCES], IMFLAG_NONE);
|
||||
mOptions[RSK_MIX_BOSS_ENTRANCES] = Option::Bool("Mix Bosses", "gRandomizeMixBosses", mOptionDescriptions[RSK_MIX_BOSS_ENTRANCES], IMFLAG_NONE);
|
||||
mOptions[RSK_MIX_OVERWORLD_ENTRANCES] = Option::Bool("Mix Overworld", "gRandomizeMixOverworld", mOptionDescriptions[RSK_MIX_OVERWORLD_ENTRANCES], IMFLAG_NONE);
|
||||
mOptions[RSK_MIX_INTERIOR_ENTRANCES] = Option::Bool("Mix Interiors", "gRandomizeMixInteriors", mOptionDescriptions[RSK_MIX_INTERIOR_ENTRANCES], IMFLAG_NONE);
|
||||
mOptions[RSK_MIX_GROTTO_ENTRANCES] = Option::Bool("Mix Grottos", "gRandomizeMixGrottos", mOptionDescriptions[RSK_MIX_GROTTO_ENTRANCES]);
|
||||
@ -638,6 +639,7 @@ void Settings::CreateOptions() {
|
||||
&mOptions[RSK_DECOUPLED_ENTRANCES],
|
||||
&mOptions[RSK_MIXED_ENTRANCE_POOLS],
|
||||
&mOptions[RSK_MIX_DUNGEON_ENTRANCES],
|
||||
&mOptions[RSK_MIX_BOSS_ENTRANCES],
|
||||
&mOptions[RSK_MIX_OVERWORLD_ENTRANCES],
|
||||
&mOptions[RSK_MIX_INTERIOR_ENTRANCES],
|
||||
&mOptions[RSK_MIX_GROTTO_ENTRANCES]
|
||||
@ -828,6 +830,7 @@ void Settings::CreateOptions() {
|
||||
&mOptions[RSK_SHUFFLE_OVERWORLD_SPAWNS],
|
||||
&mOptions[RSK_MIXED_ENTRANCE_POOLS],
|
||||
&mOptions[RSK_MIX_DUNGEON_ENTRANCES],
|
||||
&mOptions[RSK_MIX_BOSS_ENTRANCES],
|
||||
&mOptions[RSK_MIX_OVERWORLD_ENTRANCES],
|
||||
&mOptions[RSK_MIX_INTERIOR_ENTRANCES],
|
||||
&mOptions[RSK_MIX_GROTTO_ENTRANCES],
|
||||
@ -1159,6 +1162,7 @@ void Settings::CreateOptions() {
|
||||
{ "World Settings:Overworld Spawns", RSK_SHUFFLE_OVERWORLD_SPAWNS },
|
||||
{ "World Settings:Mixed Entrance Pools", RSK_MIXED_ENTRANCE_POOLS },
|
||||
{ "World Settings:Mix Dungeons", RSK_MIX_DUNGEON_ENTRANCES },
|
||||
{ "World Settings:Mix Bosses", RSK_MIX_BOSS_ENTRANCES },
|
||||
{ "World Settings:Mix Overworld", RSK_MIX_OVERWORLD_ENTRANCES },
|
||||
{ "World Settings:Mix Interiors", RSK_MIX_INTERIOR_ENTRANCES },
|
||||
{ "World Settings:Mix Grottos", RSK_MIX_GROTTO_ENTRANCES },
|
||||
@ -1534,12 +1538,14 @@ void Settings::UpdateOptionProperties() {
|
||||
if (CVarGetInteger("gRandomizeMixedEntrances", RO_GENERIC_OFF)) {
|
||||
mOptions[RSK_MIXED_ENTRANCE_POOLS].RemoveFlag(IMFLAG_SEPARATOR_BOTTOM);
|
||||
mOptions[RSK_MIX_DUNGEON_ENTRANCES].Unhide();
|
||||
mOptions[RSK_MIX_BOSS_ENTRANCES].Unhide();
|
||||
mOptions[RSK_MIX_OVERWORLD_ENTRANCES].Unhide();
|
||||
mOptions[RSK_MIX_INTERIOR_ENTRANCES].Unhide();
|
||||
mOptions[RSK_MIX_GROTTO_ENTRANCES].Unhide();
|
||||
} else {
|
||||
mOptions[RSK_MIXED_ENTRANCE_POOLS].AddFlag(IMFLAG_SEPARATOR_BOTTOM);
|
||||
mOptions[RSK_MIX_DUNGEON_ENTRANCES].Hide();
|
||||
mOptions[RSK_MIX_BOSS_ENTRANCES].Hide();
|
||||
mOptions[RSK_MIX_OVERWORLD_ENTRANCES].Hide();
|
||||
mOptions[RSK_MIX_INTERIOR_ENTRANCES].Hide();
|
||||
mOptions[RSK_MIX_GROTTO_ENTRANCES].Hide();
|
||||
@ -2309,6 +2315,7 @@ void Settings::ParseJson(nlohmann::json spoilerFileJson) {
|
||||
case RSK_SHUFFLE_OVERWORLD_SPAWNS:
|
||||
case RSK_MIXED_ENTRANCE_POOLS:
|
||||
case RSK_MIX_DUNGEON_ENTRANCES:
|
||||
case RSK_MIX_BOSS_ENTRANCES:
|
||||
case RSK_MIX_OVERWORLD_ENTRANCES:
|
||||
case RSK_MIX_INTERIOR_ENTRANCES:
|
||||
case RSK_MIX_GROTTO_ENTRANCES:
|
||||
|
@ -357,9 +357,9 @@ void SaveManager::LoadRandomizerVersion3() {
|
||||
auto entranceCtx = randoContext->GetEntranceShuffler();
|
||||
SaveManager::Instance->LoadArray("entrances", ARRAY_COUNT(entranceCtx->entranceOverrides), [&](size_t i) {
|
||||
SaveManager::Instance->LoadStruct("", [&]() {
|
||||
SaveManager::Instance->LoadData("type", entranceCtx->entranceOverrides[i].type);
|
||||
SaveManager::Instance->LoadData("index", entranceCtx->entranceOverrides[i].index);
|
||||
SaveManager::Instance->LoadData("destination", entranceCtx->entranceOverrides[i].destination);
|
||||
SaveManager::Instance->LoadData("blueWarp", entranceCtx->entranceOverrides[i].blueWarp);
|
||||
SaveManager::Instance->LoadData("override", entranceCtx->entranceOverrides[i].override);
|
||||
SaveManager::Instance->LoadData("overrideDestination",
|
||||
entranceCtx->entranceOverrides[i].overrideDestination);
|
||||
|
Loading…
Reference in New Issue
Block a user