Added minimap icons for other players in Anchor (#6372)
Icon size for other players reduced by 25%
This commit is contained in:
@@ -63,6 +63,7 @@ DEFINE_HOOK(OnDialogMessage, ());
|
||||
DEFINE_HOOK(OnPresentTitleCard, ());
|
||||
DEFINE_HOOK(OnInterfaceUpdate, ());
|
||||
DEFINE_HOOK(OnKaleidoscopeUpdate, (int16_t inDungeonScene));
|
||||
DEFINE_HOOK(OnMinimapDrawCompassIcons, ());
|
||||
|
||||
DEFINE_HOOK(OnPresentFileSelect, ());
|
||||
DEFINE_HOOK(OnUpdateFileSelectSelection, (uint16_t optionIndex));
|
||||
|
||||
@@ -298,6 +298,10 @@ void GameInteractor_ExecuteOnKaleidoscopeUpdate(int16_t inDungeonScene) {
|
||||
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnKaleidoscopeUpdate>(inDungeonScene);
|
||||
}
|
||||
|
||||
void GameInteractor_ExecuteOnMinimapDrawCompassIcons() {
|
||||
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnMinimapDrawCompassIcons>();
|
||||
}
|
||||
|
||||
// MARK: - Main Menu
|
||||
|
||||
void GameInteractor_ExecuteOnPresentFileSelect() {
|
||||
|
||||
@@ -69,6 +69,7 @@ void GameInteractor_ExecuteOnDialogMessage();
|
||||
void GameInteractor_ExecuteOnPresentTitleCard();
|
||||
void GameInteractor_ExecuteOnInterfaceUpdate();
|
||||
void GameInteractor_ExecuteOnKaleidoscopeUpdate(int16_t inDungeonScene);
|
||||
void GameInteractor_ExecuteOnMinimapDrawCompassIcons();
|
||||
|
||||
// MARK: - Main Menu
|
||||
void GameInteractor_ExecuteOnPresentFileSelect();
|
||||
|
||||
@@ -29,6 +29,7 @@ typedef struct {
|
||||
bool isSaveLoaded;
|
||||
bool isGameComplete;
|
||||
s16 sceneNum;
|
||||
s8 curRoomNum;
|
||||
s32 entranceIndex;
|
||||
|
||||
// Only available in PLAYER_UPDATE packets
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
#include "Anchor.h"
|
||||
#include <libultraship/libultraship.h>
|
||||
#include "soh/Enhancements/cosmetics/cosmeticsTypes.h"
|
||||
#include "soh/Enhancements/game-interactor/GameInteractor.h"
|
||||
#include "soh/frame_interpolation.h"
|
||||
#include "soh/OTRGlobals.h"
|
||||
|
||||
extern "C" {
|
||||
#include "variables.h"
|
||||
@@ -28,8 +31,10 @@ extern "C" {
|
||||
#include "src/overlays/actors/ovl_Obj_Hamishi/z_obj_hamishi.h"
|
||||
#include "src/overlays/actors/ovl_Bg_Hidan_Dalm/z_bg_hidan_dalm.h"
|
||||
#include "src/overlays/actors/ovl_Bg_Hidan_Kowarerukabe/z_bg_hidan_kowarerukabe.h"
|
||||
#include "objects/gameplay_keep/gameplay_keep.h"
|
||||
|
||||
extern PlayState* gPlayState;
|
||||
extern MapData* gMapData;
|
||||
|
||||
void func_8086ED70(BgBombwall* bgBombwall, PlayState* play);
|
||||
void BgBreakwall_Wait(BgBreakwall* bgBreakwall, PlayState* play);
|
||||
@@ -48,6 +53,8 @@ void BgYdanSp_FloorWebIdle(BgYdanSp* bgYdanSp, PlayState* play);
|
||||
void BgYdanSp_WallWebIdle(BgYdanSp* bgYdanSp, PlayState* play);
|
||||
void BgYdanSp_BurnWeb(BgYdanSp* bgYdanSp, PlayState* play);
|
||||
void EnDoor_Idle(EnDoor* enDoor, PlayState* play);
|
||||
float OTRGetDimensionFromLeftEdge(float v);
|
||||
float OTRGetDimensionFromRightEdge(float v);
|
||||
}
|
||||
|
||||
void Anchor::RegisterHooks() {
|
||||
@@ -393,4 +400,149 @@ void Anchor::RegisterHooks() {
|
||||
});
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region Hooks for visual effects that don't affect gameplay
|
||||
|
||||
struct CompassIcon {
|
||||
Vec3f pos;
|
||||
Vec3s rot;
|
||||
float scale;
|
||||
Color_RGB8 color;
|
||||
};
|
||||
|
||||
COND_HOOK(OnMinimapDrawCompassIcons, isConnected, [&]() {
|
||||
if (!CVarGetInteger(CVAR_REMOTE_ANCHOR("ShowOtherPlayersOnMinimap"), 1) ||
|
||||
Anchor::Instance->roomState.showLocationsMode == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<CompassIcon> compassIcons;
|
||||
|
||||
bool isInDungeon = gPlayState->sceneNum == SCENE_DEKU_TREE || gPlayState->sceneNum == SCENE_DODONGOS_CAVERN ||
|
||||
gPlayState->sceneNum == SCENE_JABU_JABU || gPlayState->sceneNum == SCENE_FOREST_TEMPLE ||
|
||||
gPlayState->sceneNum == SCENE_FIRE_TEMPLE || gPlayState->sceneNum == SCENE_WATER_TEMPLE ||
|
||||
gPlayState->sceneNum == SCENE_SPIRIT_TEMPLE || gPlayState->sceneNum == SCENE_SHADOW_TEMPLE ||
|
||||
gPlayState->sceneNum == SCENE_BOTTOM_OF_THE_WELL || gPlayState->sceneNum == SCENE_ICE_CAVERN;
|
||||
std::string teamId = CVarGetString(CVAR_REMOTE_ANCHOR("TeamId"), "default");
|
||||
|
||||
// When transitioning to a new room via a door, curRoom.num updates immediately but the minimap still shows the
|
||||
// previous room while fading out
|
||||
s8 displayedRoomNum =
|
||||
gPlayState->roomCtx.prevRoom.num >= 0 ? gPlayState->roomCtx.prevRoom.num : gPlayState->roomCtx.curRoom.num;
|
||||
|
||||
for (auto& [clientId, client] : Anchor::Instance->clients) {
|
||||
// Show compass icons for other players in the current scene. Also require them to be in the current room
|
||||
// within dungeons. If showLocationsMode isn't all players (2), only show compass icons for players of the
|
||||
// same team
|
||||
if (!client.self && client.online && client.player && client.sceneNum == gPlayState->sceneNum &&
|
||||
(!isInDungeon || client.curRoomNum == displayedRoomNum) &&
|
||||
(Anchor::Instance->roomState.showLocationsMode == 2 || client.teamId == teamId)) {
|
||||
compassIcons.push_back(
|
||||
CompassIcon{ client.player->actor.world.pos, client.player->actor.shape.rot, 0.3f, client.color });
|
||||
}
|
||||
}
|
||||
|
||||
// The local player's compass icon is always last so it gets drawn above the others
|
||||
Player* player = GET_PLAYER(gPlayState);
|
||||
compassIcons.push_back(CompassIcon{ player->actor.world.pos, player->actor.shape.rot, 0.4f,
|
||||
CVarGetColor24(CVAR_REMOTE_ANCHOR("Color.Value"), { 100, 255, 100 }) });
|
||||
|
||||
// Adapted internals of Minimap_DrawCompassIcons()
|
||||
s16 leftMinimapMargin = CVarGetInteger(CVAR_COSMETIC("HUD.Margin.L"), 0);
|
||||
s16 rightMinimapMargin = CVarGetInteger(CVAR_COSMETIC("HUD.Margin.R"), 0);
|
||||
s16 bottomMinimapMargin = CVarGetInteger(CVAR_COSMETIC("HUD.Margin.B"), 0);
|
||||
|
||||
s16 xMarginsMinimap;
|
||||
s16 yMarginsMinimap;
|
||||
if (CVarGetInteger(CVAR_COSMETIC("HUD.Minimap.UseMargins"), 0) != 0) {
|
||||
if (CVarGetInteger(CVAR_COSMETIC("HUD.Minimap.PosType"), 0) == ORIGINAL_LOCATION) {
|
||||
xMarginsMinimap = rightMinimapMargin;
|
||||
}
|
||||
yMarginsMinimap = bottomMinimapMargin;
|
||||
} else {
|
||||
xMarginsMinimap = 0;
|
||||
yMarginsMinimap = 0;
|
||||
}
|
||||
|
||||
s16 mapWidth = isInDungeon ? R_DGN_MINIMAP_X : R_OW_MINIMAP_X;
|
||||
s16 mapStartPosX = isInDungeon ? 96 : gMapData->owMinimapWidth[R_MAP_INDEX];
|
||||
|
||||
OPEN_DISPS(gPlayState->state.gfxCtx);
|
||||
Gfx_SetupDL_42Overlay(gPlayState->state.gfxCtx);
|
||||
|
||||
for (auto& compassIcon : compassIcons) {
|
||||
gSPMatrix(OVERLAY_DISP++, &gMtxClear, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
|
||||
gDPSetCombineLERP(OVERLAY_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0,
|
||||
PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0);
|
||||
gDPSetEnvColor(OVERLAY_DISP++, 0, 0, 0, 255);
|
||||
gDPSetCombineMode(OVERLAY_DISP++, G_CC_PRIMITIVE, G_CC_PRIMITIVE);
|
||||
|
||||
// The compass offset value is a factor of 10 compared to N64 screen pixels and originates in the center of
|
||||
// the screen Compute the additional mirror offset value by normalizing the original offset position and
|
||||
// taking it's distance to the center of the map, duplicating that result and casting back to a factor of 10
|
||||
s16 mirrorOffset =
|
||||
((mapWidth / 2) - ((R_COMPASS_OFFSET_X / 10) - (mapStartPosX - SCREEN_WIDTH / 2))) * 2 * 10;
|
||||
|
||||
s16 tempX = (s16)compassIcon.pos.x;
|
||||
s16 tempZ = (s16)compassIcon.pos.z;
|
||||
tempX /= R_COMPASS_SCALE_X * (CVarGetInteger(CVAR_ENHANCEMENT("MirroredWorld"), 0) ? -1 : 1);
|
||||
tempZ /= R_COMPASS_SCALE_Y;
|
||||
|
||||
s16 tempXOffset =
|
||||
R_COMPASS_OFFSET_X + (CVarGetInteger(CVAR_ENHANCEMENT("MirroredWorld"), 0) ? mirrorOffset : 0);
|
||||
if (CVarGetInteger(CVAR_COSMETIC("HUD.Minimap.PosType"), 0) != ORIGINAL_LOCATION) {
|
||||
if (CVarGetInteger(CVAR_COSMETIC("HUD.Minimap.PosType"), 0) == ANCHOR_LEFT) {
|
||||
if (CVarGetInteger(CVAR_COSMETIC("HUD.Minimap.UseMargins"), 0) != 0) {
|
||||
xMarginsMinimap = leftMinimapMargin;
|
||||
};
|
||||
Matrix_Translate(
|
||||
OTRGetDimensionFromLeftEdge((tempXOffset + (xMarginsMinimap * 10) + tempX +
|
||||
(CVarGetInteger(CVAR_COSMETIC("HUD.Minimap.PosX"), 0) * 10)) /
|
||||
10.0f),
|
||||
(R_COMPASS_OFFSET_Y + ((yMarginsMinimap * 10) * -1) - tempZ +
|
||||
((CVarGetInteger(CVAR_COSMETIC("HUD.Minimap.PosY"), 0) * 10) * -1)) /
|
||||
10.0f,
|
||||
0.0f, MTXMODE_NEW);
|
||||
} else if (CVarGetInteger(CVAR_COSMETIC("HUD.Minimap.PosType"), 0) == ANCHOR_RIGHT) {
|
||||
if (CVarGetInteger(CVAR_COSMETIC("HUD.Minimap.UseMargins"), 0) != 0) {
|
||||
xMarginsMinimap = rightMinimapMargin;
|
||||
};
|
||||
Matrix_Translate(
|
||||
OTRGetDimensionFromRightEdge((tempXOffset + (xMarginsMinimap * 10) + tempX +
|
||||
(CVarGetInteger(CVAR_COSMETIC("HUD.Minimap.PosX"), 0) * 10)) /
|
||||
10.0f),
|
||||
(R_COMPASS_OFFSET_Y + ((yMarginsMinimap * 10) * -1) - tempZ +
|
||||
((CVarGetInteger(CVAR_COSMETIC("HUD.Minimap.PosY"), 0) * 10) * -1)) /
|
||||
10.0f,
|
||||
0.0f, MTXMODE_NEW);
|
||||
} else if (CVarGetInteger(CVAR_COSMETIC("HUD.Minimap.PosType"), 0) == ANCHOR_NONE) {
|
||||
Matrix_Translate(
|
||||
(tempXOffset + tempX + (CVarGetInteger(CVAR_COSMETIC("HUD.Minimap.PosX"), 0) * 10) / 10.0f),
|
||||
(R_COMPASS_OFFSET_Y + ((yMarginsMinimap * 10) * -1) - tempZ +
|
||||
((CVarGetInteger(CVAR_COSMETIC("HUD.Minimap.PosY"), 0) * 10) * -1)) /
|
||||
10.0f,
|
||||
0.0f, MTXMODE_NEW);
|
||||
}
|
||||
} else {
|
||||
Matrix_Translate(OTRGetDimensionFromRightEdge((tempXOffset + (xMarginsMinimap * 10) + tempX) / 10.0f),
|
||||
(R_COMPASS_OFFSET_Y + ((yMarginsMinimap * 10) * -1) - tempZ) / 10.0f, 0.0f,
|
||||
MTXMODE_NEW);
|
||||
}
|
||||
Matrix_Scale(compassIcon.scale, compassIcon.scale, compassIcon.scale, MTXMODE_APPLY);
|
||||
Matrix_RotateX(-1.6f, MTXMODE_APPLY);
|
||||
s16 rotation = ((0x7FFF - compassIcon.rot.y) / 0x400) *
|
||||
(CVarGetInteger(CVAR_ENHANCEMENT("MirroredWorld"), 0) ? -1 : 1);
|
||||
Matrix_RotateY(rotation / 10.0f, MTXMODE_APPLY);
|
||||
gSPMatrix(OVERLAY_DISP++, MATRIX_NEWMTX(gPlayState->state.gfxCtx),
|
||||
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
|
||||
|
||||
gDPSetPrimColor(OVERLAY_DISP++, 0, 0xFF, compassIcon.color.r, compassIcon.color.g, compassIcon.color.b,
|
||||
255);
|
||||
gSPDisplayList(OVERLAY_DISP++, (Gfx*)gCompassArrowDL);
|
||||
}
|
||||
|
||||
CLOSE_DISPS(gPlayState->state.gfxCtx);
|
||||
});
|
||||
|
||||
// #endregion
|
||||
}
|
||||
|
||||
@@ -62,6 +62,7 @@ inline void from_json(const json& j, AnchorClient& client) {
|
||||
client.isSaveLoaded = j.value("isSaveLoaded", false);
|
||||
client.isGameComplete = j.value("isGameComplete", false);
|
||||
client.sceneNum = j.value("sceneNum", (s16)SCENE_ID_MAX);
|
||||
client.curRoomNum = j.value("curRoomNum", (s8)-1);
|
||||
client.entranceIndex = j.value("entranceIndex", (s32)0);
|
||||
client.self = j.value("self", false);
|
||||
}
|
||||
|
||||
@@ -139,6 +139,24 @@ void AnchorMainMenu(WidgetInfo& info) {
|
||||
ImGui::SameLine();
|
||||
|
||||
UIWidgets::WindowButton("Toggle Anchor Room Window", CVAR_WINDOW("AnchorRoom"), SohGui::mAnchorRoomWindow);
|
||||
|
||||
ImGui::Spacing();
|
||||
|
||||
bool hideLocations = Anchor::Instance->roomState.showLocationsMode == 0;
|
||||
ImGui::BeginDisabled(hideLocations);
|
||||
UIWidgets::CVarCheckbox(
|
||||
"Show Other Players on Minimap", CVAR_REMOTE_ANCHOR("ShowOtherPlayersOnMinimap"),
|
||||
UIWidgets::CheckboxOptions()
|
||||
.Color(THEME_COLOR)
|
||||
.DefaultValue(true)
|
||||
.Tooltip(!hideLocations
|
||||
? "Other players will appear on the minimap in areas where you have the compass. "
|
||||
"Visibility is restricted according to the Show Locations mode for the room."
|
||||
: "Cannot show other players because the room's Show Locations mode is set to None."));
|
||||
ImGui::EndDisabled();
|
||||
|
||||
ImGui::Spacing();
|
||||
|
||||
if (!SohGui::mAnchorRoomWindow->IsVisible()) {
|
||||
SohGui::mAnchorRoomWindow->DrawElement();
|
||||
}
|
||||
|
||||
@@ -33,12 +33,14 @@ nlohmann::json Anchor::PrepClientState() {
|
||||
payload["isSaveLoaded"] = true;
|
||||
payload["isGameComplete"] = gSaveContext.ship.stats.gameComplete;
|
||||
payload["sceneNum"] = gPlayState->sceneNum;
|
||||
payload["curRoomNum"] = gPlayState->roomCtx.curRoom.num;
|
||||
payload["entranceIndex"] = gSaveContext.entranceIndex;
|
||||
} else {
|
||||
payload["seed"] = 0;
|
||||
payload["isSaveLoaded"] = false;
|
||||
payload["isGameComplete"] = false;
|
||||
payload["sceneNum"] = SCENE_ID_MAX;
|
||||
payload["curRoomNum"] = -1;
|
||||
payload["entranceIndex"] = 0x00;
|
||||
}
|
||||
|
||||
@@ -68,6 +70,7 @@ void Anchor::HandlePacket_UpdateClientState(nlohmann::json payload) {
|
||||
clients[clientId].isSaveLoaded = client.isSaveLoaded;
|
||||
clients[clientId].isGameComplete = client.isGameComplete;
|
||||
clients[clientId].sceneNum = client.sceneNum;
|
||||
clients[clientId].curRoomNum = client.curRoomNum;
|
||||
clients[clientId].entranceIndex = client.entranceIndex;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <assert.h>
|
||||
#include "soh/OTRGlobals.h"
|
||||
#include "soh/Enhancements/cosmetics/cosmeticsTypes.h"
|
||||
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
|
||||
|
||||
MapData* gMapData;
|
||||
|
||||
@@ -761,6 +762,10 @@ void Minimap_DrawCompassIcons(PlayState* play) {
|
||||
}
|
||||
|
||||
CLOSE_DISPS(play->state.gfxCtx);
|
||||
|
||||
if (play->interfaceCtx.minimapAlpha >= 0xAA) {
|
||||
GameInteractor_ExecuteOnMinimapDrawCompassIcons();
|
||||
}
|
||||
}
|
||||
|
||||
void Minimap_Draw(PlayState* play) {
|
||||
|
||||
Reference in New Issue
Block a user