-
-
Notifications
You must be signed in to change notification settings - Fork 87
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
Health bars #1028
Open
olim88
wants to merge
8
commits into
SkyblockerMod:master
Choose a base branch
from
olim88:healthBars
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Health bars #1028
Changes from 7 commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
7d3689b
basic features
olim88 f5a91f2
add config and main features
olim88 e9aea10
add option to show on mobs with only a health value
olim88 1b33cb7
clean up code and add more comments
olim88 d2a82d4
increase default size
olim88 f6caa10
improve removing health code and incorrectly spell amour
olim88 b111cde
show when the nametag is shown
olim88 08873e0
add suggested changes
olim88 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
227 changes: 227 additions & 0 deletions
227
src/main/java/de/hysky/skyblocker/skyblock/HealthBars.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,227 @@ | ||
package de.hysky.skyblocker.skyblock; | ||
|
||
import de.hysky.skyblocker.annotations.Init; | ||
import de.hysky.skyblocker.config.SkyblockerConfigManager; | ||
import de.hysky.skyblocker.utils.render.RenderHelper; | ||
import it.unimi.dsi.fastutil.objects.Object2FloatMap; | ||
import it.unimi.dsi.fastutil.objects.Object2FloatOpenHashMap; | ||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; | ||
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientEntityEvents; | ||
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents; | ||
import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext; | ||
import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents; | ||
import net.minecraft.client.world.ClientWorld; | ||
import net.minecraft.entity.Entity; | ||
import net.minecraft.entity.decoration.ArmorStandEntity; | ||
import net.minecraft.text.MutableText; | ||
import net.minecraft.text.Text; | ||
import net.minecraft.util.Identifier; | ||
import net.minecraft.util.math.Vec3d; | ||
import org.apache.commons.lang3.StringUtils; | ||
|
||
import java.awt.*; | ||
import java.util.List; | ||
import java.util.regex.Matcher; | ||
import java.util.regex.Pattern; | ||
|
||
public class HealthBars { | ||
|
||
private static final Identifier HEALTH_BAR_BACKGROUND_TEXTURE = Identifier.ofVanilla("textures/gui/sprites/boss_bar/white_background.png"); | ||
private static final Identifier HEALTH_BAR_TEXTURE = Identifier.ofVanilla("textures/gui/sprites/boss_bar/white_progress.png"); | ||
private static final Pattern HEALTH_PATTERN = Pattern.compile("(\\d{1,3}(,\\d{3})*(\\.\\d+)?)/(\\d{1,3}(,\\d{3})*(\\.\\d+)?)❤"); | ||
private static final Pattern HEALTH_ONLY_PATTERN = Pattern.compile("(\\d{1,3}(,\\d{3})*(\\.\\d+)?)❤"); | ||
|
||
private static final Object2FloatOpenHashMap<ArmorStandEntity> healthValues = new Object2FloatOpenHashMap<>(); | ||
private static final Object2IntOpenHashMap<ArmorStandEntity> mobStartingHealth = new Object2IntOpenHashMap<>(); | ||
|
||
@Init | ||
public static void init() { | ||
ClientPlayConnectionEvents.JOIN.register((_handler, _sender, _client) -> reset()); | ||
WorldRenderEvents.AFTER_TRANSLUCENT.register(HealthBars::render); | ||
ClientEntityEvents.ENTITY_UNLOAD.register(HealthBars::onEntityDespawn); | ||
} | ||
|
||
private static void reset() { | ||
healthValues.clear(); | ||
mobStartingHealth.clear(); | ||
} | ||
|
||
/** | ||
* remove dead armor stands from health bars | ||
* | ||
* @param entity dying entity | ||
*/ | ||
public static void onEntityDespawn(Entity entity, ClientWorld clientWorld) { | ||
if (entity instanceof ArmorStandEntity armorStandEntity) { | ||
healthValues.removeFloat(armorStandEntity); | ||
mobStartingHealth.removeInt(armorStandEntity); | ||
} | ||
} | ||
|
||
/** | ||
* Processes armorstand updates and if it's a mob with health get the value of its health and save it the hashmap | ||
* | ||
* @param armorStand updated armorstand | ||
*/ | ||
public static void HeathBar(ArmorStandEntity armorStand) { | ||
if (!armorStand.isInvisible() || !armorStand.hasCustomName() || !armorStand.isCustomNameVisible() || !SkyblockerConfigManager.get().uiAndVisuals.healthBars.enabled) { | ||
return; | ||
} | ||
|
||
//check if armor stand is dead and remove it from list | ||
if (armorStand.isDead()) { | ||
healthValues.removeFloat(armorStand); | ||
mobStartingHealth.removeInt(armorStand); | ||
return; | ||
} | ||
|
||
//check to see if the armor stand is a mob label with health | ||
if (armorStand.getCustomName() == null) { | ||
return; | ||
} | ||
Matcher healthMatcher = HEALTH_PATTERN.matcher(armorStand.getCustomName().getString()); | ||
//if a health ratio can not be found send onto health only pattern | ||
if (!healthMatcher.find()) { | ||
HealthOnlyCheck(armorStand); | ||
return; | ||
} | ||
|
||
//work out health value and save to hashMap | ||
int firstValue = Integer.parseInt(healthMatcher.group(1).replace(",", "")); | ||
int secondValue = Integer.parseInt(healthMatcher.group(4).replace(",", "")); | ||
float health = (float) firstValue / secondValue; | ||
healthValues.put(armorStand, health); | ||
|
||
//edit armor stand name to remove health | ||
boolean removeValue = SkyblockerConfigManager.get().uiAndVisuals.healthBars.removeHealthFromName; | ||
boolean removeMax = SkyblockerConfigManager.get().uiAndVisuals.healthBars.removeMaxHealthFromName; | ||
//if both disabled no need to edit name | ||
if (!removeValue && !removeMax) { | ||
return; | ||
} | ||
MutableText cleanedText = Text.empty(); | ||
List<Text> parts = armorStand.getCustomName().getSiblings(); | ||
//loop though name and add every part to a new text skipping over the hidden health values | ||
int healthStartIndex = -1; | ||
System.out.println(healthMatcher.group(0).toString()+"testing"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. print testing my beloved |
||
for (int i = 0; i < parts.size(); i++) { | ||
//remove value from name | ||
if (i < parts.size() - 4 && StringUtils.join(parts.subList(i+1, i + 5).stream().map(Text::getString).toArray(), "").equals(healthMatcher.group(0))) { | ||
healthStartIndex = i; | ||
} | ||
if (healthStartIndex != -1) { | ||
//skip parts of the health offset form staring index | ||
switch (i - healthStartIndex) { | ||
case 0 -> { // space before health | ||
if (removeMax && removeValue) { | ||
continue; | ||
} | ||
} | ||
case 1 -> { // current health value | ||
if (removeValue) { | ||
continue; | ||
} | ||
} | ||
case 2 -> { // "/" separating health values | ||
if (removeMax) { | ||
continue; | ||
} | ||
} | ||
case 3 -> { // max health value | ||
if (removeMax) { | ||
continue; | ||
} | ||
} | ||
case 4 -> { // "❤" at end of health | ||
if (removeMax && removeValue) { | ||
continue; | ||
} | ||
} | ||
} | ||
} | ||
|
||
cleanedText.append(parts.get(i)); | ||
} | ||
armorStand.setCustomName(cleanedText); | ||
} | ||
|
||
/** | ||
* Processes armor stands that only have a health value and no max health | ||
* | ||
* @param armorStand armorstand to check the name of | ||
*/ | ||
private static void HealthOnlyCheck(ArmorStandEntity armorStand) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. camelCase |
||
if (!SkyblockerConfigManager.get().uiAndVisuals.healthBars.applyToHealthOnlyMobs || armorStand.getCustomName() == null) { | ||
return; | ||
} | ||
Matcher healthOnlyMatcher = HEALTH_ONLY_PATTERN.matcher(armorStand.getCustomName().getString()); | ||
//if not found return | ||
if (!healthOnlyMatcher.find()) { | ||
return; | ||
} | ||
|
||
//get the current health of the mob | ||
int currentHealth = Integer.parseInt(healthOnlyMatcher.group(1).replace(",", "")); | ||
|
||
//if it's a new health only armor stand add to starting health lookup (not always full health if already damaged but best that can be done) | ||
if (!mobStartingHealth.containsKey(armorStand)) { | ||
mobStartingHealth.put(armorStand, currentHealth); | ||
} | ||
|
||
//add to health bar values | ||
float health = (float) currentHealth / mobStartingHealth.getInt(armorStand); | ||
healthValues.put(armorStand, health); | ||
|
||
//if enabled remove from name | ||
if (!SkyblockerConfigManager.get().uiAndVisuals.healthBars.removeHealthFromName) { | ||
return; | ||
} | ||
MutableText cleanedText = Text.empty(); | ||
List<Text> parts = armorStand.getCustomName().getSiblings(); | ||
//loop though name and add every part to a new text skipping over the health value | ||
for (int i = 0; i < parts.size(); i++) { | ||
//skip space before value, value and heart from name | ||
if (i < parts.size() - 2 && StringUtils.join(parts.subList(i+1, i + 3).stream().map(Text::getString).toArray(), "").equals(healthOnlyMatcher.group(0))) { | ||
//skip the heart | ||
i += 2; | ||
continue; | ||
} | ||
cleanedText.append(parts.get(i)); | ||
} | ||
armorStand.setCustomName(cleanedText); | ||
} | ||
|
||
/** | ||
* Loops though armor stands with health bars and renders a bar for each of them just bellow the name label | ||
* | ||
* @param context render context | ||
*/ | ||
private static void render(WorldRenderContext context) { | ||
if (!SkyblockerConfigManager.get().uiAndVisuals.healthBars.enabled || healthValues.isEmpty()) { | ||
return; | ||
} | ||
Color barColor = SkyblockerConfigManager.get().uiAndVisuals.healthBars.barColor; | ||
boolean hideFullHealth = SkyblockerConfigManager.get().uiAndVisuals.healthBars.hideFullHealth; | ||
float scale = SkyblockerConfigManager.get().uiAndVisuals.healthBars.scale; | ||
float tickDelta = context.tickCounter().getTickDelta(false); | ||
float width = scale; | ||
float height = scale * 0.1f; | ||
|
||
for (Object2FloatMap.Entry<ArmorStandEntity> healthValue : healthValues.object2FloatEntrySet()) { | ||
//if the health bar is full and the setting is enabled to hide it stop rendering it | ||
float health = healthValue.getFloatValue(); | ||
if (hideFullHealth && health == 1) { | ||
continue; | ||
} | ||
|
||
ArmorStandEntity armorStand = healthValue.getKey(); | ||
//only render health bar if name is visible | ||
if (!armorStand.shouldRenderName()) { | ||
return; | ||
} | ||
// Render the health bar texture with scaling based on health percentage | ||
RenderHelper.renderTextureInWorld(context, armorStand.getCameraPosVec(tickDelta).add(0, 0.25 - height, 0), width, height, 1f, 1f, new Vec3d(width * -0.5f, 0, 0), HEALTH_BAR_BACKGROUND_TEXTURE, barColor, true); | ||
RenderHelper.renderTextureInWorld(context, armorStand.getCameraPosVec(tickDelta).add(0, 0.25 - height, 0), width * health, height, health, 1f, new Vec3d(width * -0.5f, 0, 0.003f), HEALTH_BAR_TEXTURE, barColor, true); | ||
} | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
camelCase