mirror of
https://github.com/HarbourMasters/Shipwright.git
synced 2024-11-25 10:52:19 -05:00
Enhancement: Enemy Health Bar (#3035)
* add enemy health bar * add more cosmetic editor options to health bar * add tooltip * fix enemy health texture when no magic bar
This commit is contained in:
parent
37f9c895d6
commit
e90c8af452
@ -178,6 +178,9 @@ typedef struct Actor {
|
||||
/* 0x134 */ ActorFunc draw; // Draw Routine. Called by `Actor_Draw`
|
||||
/* 0x138 */ ActorResetFunc reset;
|
||||
/* 0x13C */ char dbgPad[0x10]; // Padding that only exists in the debug rom
|
||||
// #region SOH [General]
|
||||
/* */ u8 maximumHealth; // Max health value for use with health bars, set on actor init
|
||||
// #endregion
|
||||
} Actor; // size = 0x14C
|
||||
|
||||
typedef enum {
|
||||
|
@ -257,6 +257,8 @@ static std::map<std::string, CosmeticOption> cosmeticOptions = {
|
||||
COSMETIC_OPTION("Hud_Minimap", "Minimap", GROUP_HUD, ImVec4( 0, 255, 255, 255), false, true, false),
|
||||
COSMETIC_OPTION("Hud_MinimapPosition", "Minimap Position", GROUP_HUD, ImVec4(200, 255, 0, 255), false, true, true),
|
||||
COSMETIC_OPTION("Hud_MinimapEntrance", "Minimap Entrance", GROUP_HUD, ImVec4(200, 0, 0, 255), false, true, true),
|
||||
COSMETIC_OPTION("Hud_EnemyHealthBar", "Enemy Health Bar", GROUP_HUD, ImVec4(255, 0, 0, 255), true, true, false),
|
||||
COSMETIC_OPTION("Hud_EnemyHealthBorder", "Enemy Health Border", GROUP_HUD, ImVec4(255, 255, 255, 255), true, false, true),
|
||||
|
||||
COSMETIC_OPTION("Title_FileChoose", "File Choose", GROUP_TITLE, ImVec4(100, 150, 255, 255), false, true, false),
|
||||
COSMETIC_OPTION("Title_NintendoLogo", "Nintendo Logo", GROUP_TITLE, ImVec4( 0, 0, 255, 255), false, true, true),
|
||||
@ -400,6 +402,10 @@ void CosmeticsUpdateTick() {
|
||||
newColor.g = sin(frequency * (hue + index) + (2 * M_PI / 3)) * 127 + 128;
|
||||
newColor.b = sin(frequency * (hue + index) + (4 * M_PI / 3)) * 127 + 128;
|
||||
newColor.a = 255;
|
||||
// For alpha supported options, retain the last set alpha instead of overwriting
|
||||
if (cosmeticOption.supportsAlpha) {
|
||||
newColor.a = cosmeticOption.currentColor.w * 255;
|
||||
}
|
||||
|
||||
cosmeticOption.currentColor.x = newColor.r / 255.0;
|
||||
cosmeticOption.currentColor.y = newColor.g / 255.0;
|
||||
@ -1425,6 +1431,32 @@ void Draw_Placements(){
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
if (ImGui::CollapsingHeader("Enemy Health Bar position")) {
|
||||
if (ImGui::BeginTable("enemyhealthbar", 1, FlagsTable)) {
|
||||
ImGui::TableSetupColumn("Enemy Health Bar settings", FlagsCell, TablesCellsWidth);
|
||||
Table_InitHeader(false);
|
||||
std::string posTypeCVar = "gCosmetics.Hud_EnemyHealthBarPosType";
|
||||
UIWidgets::EnhancementRadioButton("Anchor to Enemy", posTypeCVar.c_str(), ENEMYHEALTH_ANCHOR_ACTOR);
|
||||
UIWidgets::Tooltip("This will use enemy on screen position");
|
||||
UIWidgets::EnhancementRadioButton("Anchor to the top", posTypeCVar.c_str(), ENEMYHEALTH_ANCHOR_TOP);
|
||||
UIWidgets::Tooltip("This will make your elements follow the top edge of your game window");
|
||||
UIWidgets::EnhancementRadioButton("Anchor to the bottom", posTypeCVar.c_str(), ENEMYHEALTH_ANCHOR_BOTTOM);
|
||||
UIWidgets::Tooltip("This will make your elements follow the bottom edge of your game window");
|
||||
DrawPositionSlider("gCosmetics.Hud_EnemyHealthBar", -SCREEN_HEIGHT, SCREEN_HEIGHT, -ImGui::GetWindowViewport()->Size.x / 2, ImGui::GetWindowViewport()->Size.x / 2);
|
||||
if (UIWidgets::EnhancementSliderInt("Health Bar Width: %d", "##EnemyHealthBarWidth", "gCosmetics.Hud_EnemyHealthBarWidth.Value", 32, 128, "", 64)) {
|
||||
CVarSetInteger("gCosmetics.Hud_EnemyHealthBarWidth.Changed", 1);
|
||||
}
|
||||
UIWidgets::Tooltip("This will change the width of the health bar");
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Reset##EnemyHealthBarWidth")) {
|
||||
CVarClear("gCosmetics.Hud_EnemyHealthBarWidth.Value");
|
||||
CVarClear("gCosmetics.Hud_EnemyHealthBarWidth.Changed");
|
||||
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
|
||||
}
|
||||
ImGui::NewLine();
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DrawSillyTab() {
|
||||
@ -1539,6 +1571,10 @@ void RandomizeColor(CosmeticOption& cosmeticOption) {
|
||||
newColor.g = Random(0, 255);
|
||||
newColor.b = Random(0, 255);
|
||||
newColor.a = 255;
|
||||
// For alpha supported options, retain the last set alpha instead of overwriting
|
||||
if (cosmeticOption.supportsAlpha) {
|
||||
newColor.a = cosmeticOption.currentColor.w * 255;
|
||||
}
|
||||
|
||||
cosmeticOption.currentColor.x = newColor.r / 255.0;
|
||||
cosmeticOption.currentColor.y = newColor.g / 255.0;
|
||||
@ -1607,7 +1643,13 @@ void ResetColor(CosmeticOption& cosmeticOption) {
|
||||
}
|
||||
|
||||
void DrawCosmeticRow(CosmeticOption& cosmeticOption) {
|
||||
if (ImGui::ColorEdit3(cosmeticOption.label.c_str(), (float*)&cosmeticOption.currentColor, ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel)) {
|
||||
bool colorChanged;
|
||||
if (cosmeticOption.supportsAlpha) {
|
||||
colorChanged = ImGui::ColorEdit4(cosmeticOption.label.c_str(), (float*)&cosmeticOption.currentColor, ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel);
|
||||
} else {
|
||||
colorChanged = ImGui::ColorEdit3(cosmeticOption.label.c_str(), (float*)&cosmeticOption.currentColor, ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel);
|
||||
}
|
||||
if (colorChanged) {
|
||||
Color_RGBA8 color;
|
||||
color.r = cosmeticOption.currentColor.x * 255.0;
|
||||
color.g = cosmeticOption.currentColor.y * 255.0;
|
||||
@ -1628,6 +1670,7 @@ void DrawCosmeticRow(CosmeticOption& cosmeticOption) {
|
||||
ApplyOrResetCustomGfxPatches();
|
||||
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
|
||||
}
|
||||
if (cosmeticOption.supportsRainbow) {
|
||||
ImGui::SameLine();
|
||||
bool isRainbow = (bool)CVarGetInteger((cosmeticOption.rainbowCvar), 0);
|
||||
if (ImGui::Checkbox(("Rainbow##" + cosmeticOption.label).c_str(), &isRainbow)) {
|
||||
@ -1636,6 +1679,7 @@ void DrawCosmeticRow(CosmeticOption& cosmeticOption) {
|
||||
ApplyOrResetCustomGfxPatches();
|
||||
LUS::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesOnNextTick();
|
||||
}
|
||||
}
|
||||
ImGui::SameLine();
|
||||
bool isLocked = (bool)CVarGetInteger((cosmeticOption.lockedCvar), 0);
|
||||
if (ImGui::Checkbox(("Locked##" + cosmeticOption.label).c_str(), &isLocked)) {
|
||||
|
@ -2,3 +2,9 @@ typedef enum {
|
||||
COLORSCHEME_N64,
|
||||
COLORSCHEME_GAMECUBE
|
||||
} DefaultColorScheme;
|
||||
|
||||
typedef enum {
|
||||
ENEMYHEALTH_ANCHOR_ACTOR,
|
||||
ENEMYHEALTH_ANCHOR_TOP,
|
||||
ENEMYHEALTH_ANCHOR_BOTTOM,
|
||||
} EnemyHealthBarAnchorType;
|
||||
|
@ -865,6 +865,8 @@ void DrawEnhancementsMenu() {
|
||||
|
||||
UIWidgets::PaddedEnhancementCheckbox("Disable Crit wiggle", "gDisableCritWiggle", true, false);
|
||||
UIWidgets::Tooltip("Disable random camera wiggle at low health");
|
||||
UIWidgets::PaddedEnhancementCheckbox("Enemy Health Bars", "gEnemyHealthBar", true, false);
|
||||
UIWidgets::Tooltip("Renders a health bar for enemies when Z-Targeted");
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
@ -1212,6 +1212,11 @@ void Actor_Init(Actor* actor, PlayState* play) {
|
||||
//Actor_SetObjectDependency(play, actor);
|
||||
actor->init(actor, play);
|
||||
actor->init = NULL;
|
||||
|
||||
// For enemy health bar we need to know the max health during init
|
||||
if (actor->category == ACTORCAT_ENEMY) {
|
||||
actor->maximumHealth = actor->colChkInfo.health;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3643,6 +3643,175 @@ void Interface_DrawMagicBar(PlayState* play) {
|
||||
CLOSE_DISPS(play->state.gfxCtx);
|
||||
}
|
||||
|
||||
static Vtx sEnemyHealthVtx[12];
|
||||
|
||||
// Build vertex coordinates for a quad command
|
||||
// In order of top left, top right, bottom left, then bottom right
|
||||
// Supports flipping the texture horizontally
|
||||
void Interface_CreateQuadVertexGroup(Vtx* vtxList, s32 xStart, s32 yStart, s32 width, s32 height, u8 flippedH) {
|
||||
vtxList[0].v.ob[0] = xStart;
|
||||
vtxList[0].v.ob[1] = yStart;
|
||||
vtxList[0].v.tc[0] = (flippedH ? width : 0) << 5;
|
||||
vtxList[0].v.tc[1] = 0 << 5;
|
||||
|
||||
vtxList[1].v.ob[0] = xStart + width;
|
||||
vtxList[1].v.ob[1] = yStart;
|
||||
vtxList[1].v.tc[0] = (flippedH ? width * 2 : width) << 5;
|
||||
vtxList[1].v.tc[1] = 0 << 5;
|
||||
|
||||
vtxList[2].v.ob[0] = xStart;
|
||||
vtxList[2].v.ob[1] = yStart + height;
|
||||
vtxList[2].v.tc[0] = (flippedH ? width : 0) << 5;
|
||||
vtxList[2].v.tc[1] = height << 5;
|
||||
|
||||
vtxList[3].v.ob[0] = xStart + width;
|
||||
vtxList[3].v.ob[1] = yStart + height;
|
||||
vtxList[3].v.tc[0] = (flippedH ? width * 2 : width) << 5;
|
||||
vtxList[3].v.tc[1] = height << 5;
|
||||
}
|
||||
|
||||
// Draws an enemy health bar using the magic bar textures and positions it in a similar way to Z-Targeting
|
||||
void Interface_DrawEnemyHealthBar(TargetContext* targetCtx, PlayState* play) {
|
||||
InterfaceContext* interfaceCtx = &play->interfaceCtx;
|
||||
Player* player = GET_PLAYER(play);
|
||||
Actor* actor = targetCtx->targetedActor;
|
||||
|
||||
Vec3f projTargetCenter;
|
||||
f32 projTargetCappedInvW;
|
||||
|
||||
Color_RGBA8 healthbar_red = { 255, 0, 0, 255 };
|
||||
Color_RGBA8 healthbar_border = { 255, 255, 255, 255 };
|
||||
s16 healthbar_fillWidth = 64;
|
||||
s16 healthbar_actorOffset = 40;
|
||||
s32 healthbar_offsetX = CVarGetInteger("gCosmetics.Hud_EnemyHealthBarPosX", 0);
|
||||
s32 healthbar_offsetY = CVarGetInteger("gCosmetics.Hud_EnemyHealthBarPosY", 0);
|
||||
s8 anchorType = CVarGetInteger("gCosmetics.Hud_EnemyHealthBarPosType", ENEMYHEALTH_ANCHOR_ACTOR);
|
||||
|
||||
if (CVarGetInteger("gCosmetics.Hud_EnemyHealthBar.Changed", 0)) {
|
||||
healthbar_red = CVarGetColor("gCosmetics.Hud_EnemyHealthBar.Value", healthbar_red);
|
||||
}
|
||||
if (CVarGetInteger("gCosmetics.Hud_EnemyHealthBorder.Changed", 0)) {
|
||||
healthbar_border = CVarGetColor("gCosmetics.Hud_EnemyHealthBorder.Value", healthbar_border);
|
||||
}
|
||||
if (CVarGetInteger("gCosmetics.Hud_EnemyHealthBarWidth.Changed", 0)) {
|
||||
healthbar_fillWidth = CVarGetInteger("gCosmetics.Hud_EnemyHealthBarWidth.Value", healthbar_fillWidth);
|
||||
}
|
||||
|
||||
OPEN_DISPS(play->state.gfxCtx);
|
||||
|
||||
if (targetCtx->unk_48 != 0 && actor != NULL && actor->category == ACTORCAT_ENEMY) {
|
||||
s16 texHeight = 16;
|
||||
s16 endTexWidth = 8;
|
||||
f32 scaleY = -0.75f;
|
||||
f32 scaledHeight = -texHeight * scaleY;
|
||||
f32 halfBarWidth = endTexWidth + (healthbar_fillWidth / 2);
|
||||
s16 healthBarFill = ((f32)actor->colChkInfo.health / actor->maximumHealth) * healthbar_fillWidth;
|
||||
|
||||
if (anchorType == ENEMYHEALTH_ANCHOR_ACTOR) {
|
||||
// Get actor projected position
|
||||
func_8002BE04(play, &targetCtx->targetCenterPos, &projTargetCenter, &projTargetCappedInvW);
|
||||
|
||||
projTargetCenter.x = (SCREEN_WIDTH / 2) * (projTargetCenter.x * projTargetCappedInvW);
|
||||
projTargetCenter.x = projTargetCenter.x * (CVarGetInteger("gMirroredWorld", 0) ? -1 : 1);
|
||||
projTargetCenter.x = CLAMP(projTargetCenter.x, (-SCREEN_WIDTH / 2) + halfBarWidth,
|
||||
(SCREEN_WIDTH / 2) - halfBarWidth);
|
||||
|
||||
projTargetCenter.y = (SCREEN_HEIGHT / 2) * (projTargetCenter.y * projTargetCappedInvW);
|
||||
projTargetCenter.y = projTargetCenter.y - healthbar_offsetY + healthbar_actorOffset;
|
||||
projTargetCenter.y = CLAMP(projTargetCenter.y, (-SCREEN_HEIGHT / 2) + (scaledHeight / 2),
|
||||
(SCREEN_HEIGHT / 2) - (scaledHeight / 2));
|
||||
} else if (anchorType == ENEMYHEALTH_ANCHOR_TOP) {
|
||||
projTargetCenter.x = healthbar_offsetX;
|
||||
projTargetCenter.y = (SCREEN_HEIGHT / 2) - (scaledHeight / 2) - healthbar_offsetY;
|
||||
} else if (anchorType == ENEMYHEALTH_ANCHOR_BOTTOM) {
|
||||
projTargetCenter.x = healthbar_offsetX;
|
||||
projTargetCenter.y = (-SCREEN_HEIGHT / 2) + (scaledHeight / 2) - healthbar_offsetY;
|
||||
}
|
||||
|
||||
// Health bar border end left
|
||||
Interface_CreateQuadVertexGroup(&sEnemyHealthVtx[0], -halfBarWidth, -texHeight / 2, endTexWidth, texHeight, 0);
|
||||
// Health bar border middle
|
||||
Interface_CreateQuadVertexGroup(&sEnemyHealthVtx[4], -halfBarWidth + endTexWidth, -texHeight / 2,
|
||||
healthbar_fillWidth, texHeight, 0);
|
||||
// Health bar border end right
|
||||
Interface_CreateQuadVertexGroup(&sEnemyHealthVtx[8], halfBarWidth - endTexWidth, -texHeight / 2, endTexWidth,
|
||||
texHeight, 1);
|
||||
// Health bar fill
|
||||
Interface_CreateQuadVertexGroup(&sEnemyHealthVtx[12], -halfBarWidth + endTexWidth, (-texHeight / 2) + 3,
|
||||
healthBarFill, 7, 0);
|
||||
|
||||
if (((!(player->stateFlags1 & 0x40)) || (actor != player->unk_664)) && targetCtx->unk_44 < 500.0f) {
|
||||
f32 slideInOffsetY = 0;
|
||||
|
||||
// Slide in the health bar from edge of the screen (mimic the Z-Target triangles fly in)
|
||||
if (anchorType == ENEMYHEALTH_ANCHOR_ACTOR && targetCtx->unk_44 > 120.0f) {
|
||||
slideInOffsetY = (targetCtx->unk_44 - 120.0f) / 2;
|
||||
// Slide in from the top if the bar is placed on the top half of the screen
|
||||
if (healthbar_offsetY - healthbar_actorOffset <= 0) {
|
||||
slideInOffsetY *= -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Setup DL for overlay disp
|
||||
Gfx_SetupDL_39Overlay(play->state.gfxCtx);
|
||||
|
||||
Matrix_Translate(projTargetCenter.x, projTargetCenter.y - slideInOffsetY, 0, MTXMODE_NEW);
|
||||
Matrix_Scale(1.0f, scaleY, 1.0f, MTXMODE_APPLY);
|
||||
gSPMatrix(OVERLAY_DISP++, MATRIX_NEWMTX(play->state.gfxCtx), G_MTX_MODELVIEW | G_MTX_LOAD);
|
||||
|
||||
// Health bar border
|
||||
gDPPipeSync(OVERLAY_DISP++);
|
||||
gDPSetPrimColor(OVERLAY_DISP++, 0, 0, healthbar_border.r, healthbar_border.g, healthbar_border.b,
|
||||
healthbar_border.a);
|
||||
gDPSetEnvColor(OVERLAY_DISP++, 100, 50, 50, 255);
|
||||
|
||||
gSPVertex(OVERLAY_DISP++, sEnemyHealthVtx, 16, 0);
|
||||
|
||||
gDPLoadTextureBlock(OVERLAY_DISP++, gMagicMeterEndTex, G_IM_FMT_IA, G_IM_SIZ_8b, endTexWidth, texHeight, 0,
|
||||
G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK,
|
||||
G_TX_NOLOD, G_TX_NOLOD);
|
||||
|
||||
gSP1Quadrangle(OVERLAY_DISP++, 0, 2, 3, 1, 0);
|
||||
|
||||
gDPLoadTextureBlock(OVERLAY_DISP++, gMagicMeterMidTex, G_IM_FMT_IA, G_IM_SIZ_8b, 24, texHeight, 0,
|
||||
G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK,
|
||||
G_TX_NOLOD, G_TX_NOLOD);
|
||||
|
||||
gSP1Quadrangle(OVERLAY_DISP++, 4, 6, 7, 5, 0);
|
||||
|
||||
gDPLoadTextureBlock(OVERLAY_DISP++, gMagicMeterEndTex, G_IM_FMT_IA, G_IM_SIZ_8b, endTexWidth, texHeight, 0,
|
||||
G_TX_MIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, 3, G_TX_NOMASK, G_TX_NOLOD,
|
||||
G_TX_NOLOD);
|
||||
|
||||
gSP1Quadrangle(OVERLAY_DISP++, 8, 10, 11, 9, 0);
|
||||
|
||||
// Health bar fill
|
||||
Matrix_Push();
|
||||
Matrix_Translate(-0.375f, -0.5f, 0, MTXMODE_APPLY);
|
||||
gSPMatrix(OVERLAY_DISP++, MATRIX_NEWMTX(play->state.gfxCtx), G_MTX_MODELVIEW | G_MTX_LOAD);
|
||||
|
||||
gDPPipeSync(OVERLAY_DISP++);
|
||||
gDPSetCombineLERP(OVERLAY_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, 0, 0, 0, PRIMITIVE,
|
||||
PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, 0, 0, 0, PRIMITIVE);
|
||||
gDPSetEnvColor(OVERLAY_DISP++, 0, 0, 0, 255);
|
||||
|
||||
gDPSetPrimColor(OVERLAY_DISP++, 0, 0, healthbar_red.r, healthbar_red.g, healthbar_red.b, healthbar_red.a);
|
||||
|
||||
gDPLoadMultiBlock_4b(OVERLAY_DISP++, gMagicMeterFillTex, 0, G_TX_RENDERTILE, G_IM_FMT_I, 16, texHeight, 0,
|
||||
G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK,
|
||||
G_TX_NOLOD, G_TX_NOLOD);
|
||||
|
||||
gSPVertex(OVERLAY_DISP++, &sEnemyHealthVtx[12], 4, 0);
|
||||
|
||||
gSP1Quadrangle(OVERLAY_DISP++, 0, 2, 3, 1, 0);
|
||||
|
||||
Matrix_Pop();
|
||||
}
|
||||
}
|
||||
|
||||
CLOSE_DISPS(play->state.gfxCtx);
|
||||
}
|
||||
|
||||
void func_80088AA0(s16 arg0) {
|
||||
gSaveContext.timerX[1] = 140;
|
||||
gSaveContext.timerY[1] = 80;
|
||||
@ -5096,6 +5265,11 @@ void Interface_Draw(PlayState* play) {
|
||||
if (CVarGetInteger("gMirroredWorld", 0)) {
|
||||
gSPMatrix(OVERLAY_DISP++, interfaceCtx->view.projectionPtr, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_PROJECTION);
|
||||
}
|
||||
|
||||
// Render enemy health bar after Z-target to leverage set variables
|
||||
if (CVarGetInteger("gEnemyHealthBar", 0)) {
|
||||
Interface_DrawEnemyHealthBar(&play->actorCtx.targetCtx, play);
|
||||
}
|
||||
}
|
||||
|
||||
Gfx_SetupDL_39Overlay(play->state.gfxCtx);
|
||||
|
Loading…
Reference in New Issue
Block a user