aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsyeyoung <cyong06@naver.com>2021-01-04 21:45:04 +0900
committersyeyoung <cyong06@naver.com>2021-01-04 21:45:04 +0900
commit04d414547e4d932f4db9a74dbc4aea8d75b96960 (patch)
treeacfccab2803dc6161e4ce099021d2fa972967ace
parentece0af65a8bba6c722ac6f0853c570d2e8cad784 (diff)
downloadSkyblock-Dungeons-Guide-04d414547e4d932f4db9a74dbc4aea8d75b96960.tar.gz
Skyblock-Dungeons-Guide-04d414547e4d932f4db9a74dbc4aea8d75b96960.tar.bz2
Skyblock-Dungeons-Guide-04d414547e4d932f4db9a74dbc4aea8d75b96960.zip
Various things
-rwxr-xr-xoptions.txt2
-rwxr-xr-xsrc/main/java/kr/syeyoung/dungeonsguide/dungeon/DungeonContext.java13
-rwxr-xr-xsrc/main/java/kr/syeyoung/dungeonsguide/dungeon/MapProcessor.java47
-rwxr-xr-xsrc/main/java/kr/syeyoung/dungeonsguide/dungeon/data/DungeonRoomInfo.java1
-rwxr-xr-xsrc/main/java/kr/syeyoung/dungeonsguide/dungeon/doorfinder/CatacombDataProvider.java (renamed from src/main/java/kr/syeyoung/dungeonsguide/dungeon/doorfinder/CatacombDoorFinder.java)8
-rwxr-xr-xsrc/main/java/kr/syeyoung/dungeonsguide/dungeon/doorfinder/DoorFinderRegistry.java20
-rwxr-xr-xsrc/main/java/kr/syeyoung/dungeonsguide/dungeon/doorfinder/DungeonSpecificDataProvider.java (renamed from src/main/java/kr/syeyoung/dungeonsguide/dungeon/doorfinder/StartDoorFinder.java)6
-rwxr-xr-xsrc/main/java/kr/syeyoung/dungeonsguide/dungeon/doorfinder/DungeonSpecificDataProviderRegistry.java20
-rwxr-xr-xsrc/main/java/kr/syeyoung/dungeonsguide/dungeon/roomfinder/DungeonRoom.java15
-rwxr-xr-xsrc/main/java/kr/syeyoung/dungeonsguide/eventlistener/DungeonListener.java13
-rw-r--r--src/main/java/kr/syeyoung/dungeonsguide/eventlistener/FeatureListener.java6
-rw-r--r--src/main/java/kr/syeyoung/dungeonsguide/features/FeatureRegistry.java23
-rw-r--r--src/main/java/kr/syeyoung/dungeonsguide/features/GuiFeature.java14
-rw-r--r--src/main/java/kr/syeyoung/dungeonsguide/features/impl/FeatureDungeonDeaths.java96
-rw-r--r--src/main/java/kr/syeyoung/dungeonsguide/features/impl/FeatureDungeonMilestone.java74
-rw-r--r--src/main/java/kr/syeyoung/dungeonsguide/features/impl/FeatureDungeonRealTime.java57
-rw-r--r--src/main/java/kr/syeyoung/dungeonsguide/features/impl/FeatureDungeonSBTime.java92
-rw-r--r--src/main/java/kr/syeyoung/dungeonsguide/features/impl/FeatureDungeonScore.java167
-rw-r--r--src/main/java/kr/syeyoung/dungeonsguide/features/impl/FeatureDungeonSecrets.java71
-rw-r--r--src/main/java/kr/syeyoung/dungeonsguide/features/impl/FeatureDungeonTombs.java50
-rwxr-xr-xsrc/main/java/kr/syeyoung/dungeonsguide/roomprocessor/GeneralRoomProcessor.java33
-rwxr-xr-xsrc/main/java/kr/syeyoung/dungeonsguide/roomprocessor/RoomProcessor.java1
-rwxr-xr-xsrc/main/java/kr/syeyoung/dungeonsguide/utils/TextUtils.java19
23 files changed, 788 insertions, 60 deletions
diff --git a/options.txt b/options.txt
index aa66b49f..7bbf35e6 100755
--- a/options.txt
+++ b/options.txt
@@ -91,7 +91,7 @@ key_key.hotbar.7:8
key_key.hotbar.8:9
key_key.hotbar.9:10
key_start editing session:19
-soundCategory_master:1.0
+soundCategory_master:0.0
soundCategory_music:0.0
soundCategory_record:1.0
soundCategory_weather:1.0
diff --git a/src/main/java/kr/syeyoung/dungeonsguide/dungeon/DungeonContext.java b/src/main/java/kr/syeyoung/dungeonsguide/dungeon/DungeonContext.java
index 59ce37c6..7a6d704f 100755
--- a/src/main/java/kr/syeyoung/dungeonsguide/dungeon/DungeonContext.java
+++ b/src/main/java/kr/syeyoung/dungeonsguide/dungeon/DungeonContext.java
@@ -5,12 +5,11 @@ import kr.syeyoung.dungeonsguide.roomprocessor.RoomProcessor;
import lombok.Getter;
import lombok.Setter;
import net.minecraft.util.BlockPos;
+import net.minecraft.util.IntegerCache;
import net.minecraft.world.World;
import java.awt.*;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Map;
+import java.util.*;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
@@ -32,6 +31,14 @@ public class DungeonContext {
@Getter
private List<RoomProcessor> globalRoomProcessors = new ArrayList<RoomProcessor>();
+ @Getter
+ private Map<String, Integer> deaths = new HashMap<String, Integer>();
+ @Getter
+ private List<String[]> milestoneReached = new ArrayList<String[]>();
+ @Getter
+ @Setter
+ private int BossRoomEnterSeconds;
+
public DungeonContext(World world) {
this.world = world;
mapProcessor = new MapProcessor(this);
diff --git a/src/main/java/kr/syeyoung/dungeonsguide/dungeon/MapProcessor.java b/src/main/java/kr/syeyoung/dungeonsguide/dungeon/MapProcessor.java
index 698b78ab..0bf0d9e5 100755
--- a/src/main/java/kr/syeyoung/dungeonsguide/dungeon/MapProcessor.java
+++ b/src/main/java/kr/syeyoung/dungeonsguide/dungeon/MapProcessor.java
@@ -4,9 +4,10 @@ import com.google.common.collect.Sets;
import kr.syeyoung.dungeonsguide.e;
import kr.syeyoung.dungeonsguide.SkyblockStatus;
import kr.syeyoung.dungeonsguide.dungeon.roomfinder.DungeonRoom;
-import kr.syeyoung.dungeonsguide.dungeon.doorfinder.DoorFinderRegistry;
-import kr.syeyoung.dungeonsguide.dungeon.doorfinder.StartDoorFinder;
+import kr.syeyoung.dungeonsguide.dungeon.doorfinder.DungeonSpecificDataProviderRegistry;
+import kr.syeyoung.dungeonsguide.dungeon.doorfinder.DungeonSpecificDataProvider;
import kr.syeyoung.dungeonsguide.utils.MapUtils;
+import lombok.Getter;
import net.minecraft.client.Minecraft;
import net.minecraft.item.ItemMap;
import net.minecraft.item.ItemStack;
@@ -35,12 +36,21 @@ public class MapProcessor {
private boolean axisMatch = false;
+ @Getter
+ private int undiscoveredRoom = 0;
+
public MapProcessor(DungeonContext context) {
this.context = context;
}
private static final Set<Vector2d> directions = Sets.newHashSet(new Vector2d(0,1), new Vector2d(0, -1), new Vector2d(1, 0), new Vector2d(-1 , 0));
+ public boolean isInBossRoom() {
+ Point roomPt = worldPointToRoomPoint(Minecraft.getMinecraft().thePlayer.getPosition());
+ Point mapPt = roomPointToMapPoint(roomPt);
+ return mapPt.x < 0 || mapPt.y < 0 || mapPt.x > 128 || mapPt.y > 128;
+ }
+
private int waitCnt = 0;
private void buildMap(final byte[] mapData) {
final Point startroom = MapUtils.findFirstColorWithIn(mapData, (byte) 30, new Rectangle(0,0,128,128));
@@ -98,13 +108,13 @@ public class MapProcessor {
}
// determine door location based on npc, and determine map min from there
{
- StartDoorFinder doorFinder = DoorFinderRegistry.getDoorFinder(((SkyblockStatus) e.getDungeonsGuide().getSkyblockStatus()).getDungeonName());
+ DungeonSpecificDataProvider doorFinder = DungeonSpecificDataProviderRegistry.getDoorFinder(((SkyblockStatus) e.getDungeonsGuide().getSkyblockStatus()).getDungeonName());
if (doorFinder == null) {
Minecraft.getMinecraft().thePlayer.addChatMessage(new ChatComponentText("§eDungeons Guide §7:: §cCouldn't find door processor for "+e.getDungeonsGuide().getSkyblockStatus().getDungeonName()));
bugged = true;
return;
}
- BlockPos door = doorFinder.find(context.getWorld());
+ BlockPos door = doorFinder.findDoor(context.getWorld(), e.getDungeonsGuide().getSkyblockStatus().getDungeonName());
if (door == null) {
Minecraft.getMinecraft().thePlayer.addChatMessage(new ChatComponentText("§eDungeons Guide §7:: §cCouldn't determine door of the room, disabling mod for this dungeon run"));
bugged = true;
@@ -117,7 +127,7 @@ public class MapProcessor {
unitPoint.translate(unitPoint.x + 1, unitPoint.y + 1);
unitPoint.translate((int)doorDir.x, (int)doorDir.y);
- Vector2d offset = doorFinder.offset(context.getWorld());
+ Vector2d offset = doorFinder.findDoorOffset(context.getWorld(), e.getDungeonsGuide().getSkyblockStatus().getDungeonName());
axisMatch = doorDir.equals(offset);
int worldX = unitPoint.x * 16;
@@ -155,14 +165,34 @@ public class MapProcessor {
private void processMap(byte[] mapData) {
int height = (int)((128.0 - topLeftMapPoint.y) / (unitRoomDimension.height + doorDimension.height));
int width = (int) ((128.0 - topLeftMapPoint.x) / (unitRoomDimension.width + doorDimension.height));
+ undiscoveredRoom = 0;
if (MapUtils.getMapColorAt(mapData,0,0) != 0) return;
for (int y = 0; y <= height; y++){
for (int x = 0; x <= width; x++) {
- if (roomsFound.contains(new Point(x,y))) continue;
-
Point mapPoint = roomPointToMapPoint(new Point(x,y));
byte color = MapUtils.getMapColorAt(mapData, mapPoint.x, mapPoint.y);
MapUtils.record(mapData, mapPoint.x, mapPoint.y, new Color(255,255,0,80));
+ if (roomsFound.contains(new Point(x,y))) {
+ DungeonRoom dungeonRoom = context.getRoomMapper().get(new Point(x,y));
+ if (color == 30 || color == 18) {
+ dungeonRoom.setCurrentState(DungeonRoom.RoomState.FINISHED);
+ } else {
+ byte centerColor = MapUtils.getMapColorAt(mapData, mapPoint.x + unitRoomDimension.width / 2, mapPoint.y + unitRoomDimension.height / 2);
+ MapUtils.record(mapData, mapPoint.x + unitRoomDimension.width / 2, mapPoint.y + unitRoomDimension.height / 2, new Color(0,255,0,80));
+ if (centerColor == 34) {
+ dungeonRoom.setCurrentState(DungeonRoom.RoomState.COMPLETE_WITHOUT_SECRETS);
+ } else if (centerColor == 30) {
+ dungeonRoom.setCurrentState(DungeonRoom.RoomState.FINISHED);
+ } else if (centerColor == 18) { // red
+ dungeonRoom.setCurrentState(DungeonRoom.RoomState.FAILED);
+ }
+ }
+ if (dungeonRoom.getTotalSecrets() == -1) {
+ MapUtils.record(mapData, mapPoint.x, mapPoint.y +1, new Color(0,255,0,80));
+ }
+ continue;
+ }
+
if (color != 0 && color != 85) {
MapUtils.record(mapData, mapPoint.x, mapPoint.y, new Color(0,255,255,80));
DungeonRoom rooms = buildRoom(mapData, new Point(x,y));
@@ -185,11 +215,12 @@ public class MapProcessor {
}
if (rooms.getRoomProcessor() != null && rooms.getRoomProcessor().readGlobalChat())
context.getGlobalRoomProcessors().add(rooms.getRoomProcessor());
+ } else if (color == 85){
+ undiscoveredRoom++;
}
}
}
-
}
private DungeonRoom buildRoom(byte[] mapData, Point unitPoint) {
diff --git a/src/main/java/kr/syeyoung/dungeonsguide/dungeon/data/DungeonRoomInfo.java b/src/main/java/kr/syeyoung/dungeonsguide/dungeon/data/DungeonRoomInfo.java
index 05cd57d6..71b51255 100755
--- a/src/main/java/kr/syeyoung/dungeonsguide/dungeon/data/DungeonRoomInfo.java
+++ b/src/main/java/kr/syeyoung/dungeonsguide/dungeon/data/DungeonRoomInfo.java
@@ -38,4 +38,5 @@ public class DungeonRoomInfo implements Serializable {
private Map<String, Object> properties = new HashMap<String, Object>();
private Map<String, DungeonMechanic> mechanics = new HashMap<String, DungeonMechanic>();
+ private int totalSecrets = -1;
}
diff --git a/src/main/java/kr/syeyoung/dungeonsguide/dungeon/doorfinder/CatacombDoorFinder.java b/src/main/java/kr/syeyoung/dungeonsguide/dungeon/doorfinder/CatacombDataProvider.java
index b73fa2aa..7f4e9813 100755
--- a/src/main/java/kr/syeyoung/dungeonsguide/dungeon/doorfinder/CatacombDoorFinder.java
+++ b/src/main/java/kr/syeyoung/dungeonsguide/dungeon/doorfinder/CatacombDataProvider.java
@@ -2,23 +2,21 @@ package kr.syeyoung.dungeonsguide.dungeon.doorfinder;
import com.google.common.base.Predicate;
import com.google.common.collect.Sets;
-import net.minecraft.client.Minecraft;
import net.minecraft.entity.item.EntityArmorStand;
import net.minecraft.init.Blocks;
import net.minecraft.util.BlockPos;
-import net.minecraft.util.ChatComponentText;
import net.minecraft.world.World;
import javax.vecmath.Vector2d;
import java.util.Collection;
import java.util.Set;
-public class CatacombDoorFinder implements StartDoorFinder {
+public class CatacombDataProvider implements DungeonSpecificDataProvider {
private static final Set<Vector2d> directions = Sets.newHashSet(new Vector2d(0,1), new Vector2d(0, -1), new Vector2d(1, 0), new Vector2d(-1 , 0));
@Override
- public BlockPos find(World w) {
+ public BlockPos findDoor(World w, String dungeonName) {
Collection<EntityArmorStand> armorStand = w.getEntities(EntityArmorStand.class, new Predicate<EntityArmorStand>() {
@Override
public boolean apply(EntityArmorStand input) {
@@ -43,7 +41,7 @@ public class CatacombDoorFinder implements StartDoorFinder {
}
@Override
- public Vector2d offset(World w) {
+ public Vector2d findDoorOffset(World w, String dungeonName) {
Collection<EntityArmorStand> armorStand = w.getEntities(EntityArmorStand.class, new Predicate<EntityArmorStand>() {
@Override
public boolean apply(EntityArmorStand input) {
diff --git a/src/main/java/kr/syeyoung/dungeonsguide/dungeon/doorfinder/DoorFinderRegistry.java b/src/main/java/kr/syeyoung/dungeonsguide/dungeon/doorfinder/DoorFinderRegistry.java
deleted file mode 100755
index 0fb3c89a..00000000
--- a/src/main/java/kr/syeyoung/dungeonsguide/dungeon/doorfinder/DoorFinderRegistry.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package kr.syeyoung.dungeonsguide.dungeon.doorfinder;
-
-import java.util.HashMap;
-import java.util.Map;
-import java.util.regex.Pattern;
-
-public class DoorFinderRegistry {
- private static final Map<Pattern, StartDoorFinder> doorFinders = new HashMap<Pattern, StartDoorFinder>();
-
- static {
- doorFinders.put(Pattern.compile("The Catacombs (?:F[0-9]|E)"), new CatacombDoorFinder());
- }
-
- public static StartDoorFinder getDoorFinder(String dungeonName) {
- for (Map.Entry<Pattern, StartDoorFinder> doorFinderEntry :doorFinders.entrySet()){
- if (doorFinderEntry.getKey().matcher(dungeonName).matches()) return doorFinderEntry.getValue();
- }
- return null;
- }
-}
diff --git a/src/main/java/kr/syeyoung/dungeonsguide/dungeon/doorfinder/StartDoorFinder.java b/src/main/java/kr/syeyoung/dungeonsguide/dungeon/doorfinder/DungeonSpecificDataProvider.java
index ee1ccdaa..cc97d73a 100755
--- a/src/main/java/kr/syeyoung/dungeonsguide/dungeon/doorfinder/StartDoorFinder.java
+++ b/src/main/java/kr/syeyoung/dungeonsguide/dungeon/doorfinder/DungeonSpecificDataProvider.java
@@ -5,7 +5,7 @@ import net.minecraft.world.World;
import javax.vecmath.Vector2d;
-public interface StartDoorFinder {
- BlockPos find(World w);
- Vector2d offset(World w);
+public interface DungeonSpecificDataProvider {
+ BlockPos findDoor(World w, String dungeonName);
+ Vector2d findDoorOffset(World w, String dungeonName);
}
diff --git a/src/main/java/kr/syeyoung/dungeonsguide/dungeon/doorfinder/DungeonSpecificDataProviderRegistry.java b/src/main/java/kr/syeyoung/dungeonsguide/dungeon/doorfinder/DungeonSpecificDataProviderRegistry.java
new file mode 100755
index 00000000..41048bd2
--- /dev/null
+++ b/src/main/java/kr/syeyoung/dungeonsguide/dungeon/doorfinder/DungeonSpecificDataProviderRegistry.java
@@ -0,0 +1,20 @@
+package kr.syeyoung.dungeonsguide.dungeon.doorfinder;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+public class DungeonSpecificDataProviderRegistry {
+ private static final Map<Pattern, DungeonSpecificDataProvider> doorFinders = new HashMap<Pattern, DungeonSpecificDataProvider>();
+
+ static {
+ doorFinders.put(Pattern.compile("The Catacombs (?:F[0-9]|E)"), new CatacombDataProvider());
+ }
+
+ public static DungeonSpecificDataProvider getDoorFinder(String dungeonName) {
+ for (Map.Entry<Pattern, DungeonSpecificDataProvider> doorFinderEntry :doorFinders.entrySet()){
+ if (doorFinderEntry.getKey().matcher(dungeonName).matches()) return doorFinderEntry.getValue();
+ }
+ return null;
+ }
+}
diff --git a/src/main/java/kr/syeyoung/dungeonsguide/dungeon/roomfinder/DungeonRoom.java b/src/main/java/kr/syeyoung/dungeonsguide/dungeon/roomfinder/DungeonRoom.java
index c6afb59f..6b0322ee 100755
--- a/src/main/java/kr/syeyoung/dungeonsguide/dungeon/roomfinder/DungeonRoom.java
+++ b/src/main/java/kr/syeyoung/dungeonsguide/dungeon/roomfinder/DungeonRoom.java
@@ -8,7 +8,9 @@ import kr.syeyoung.dungeonsguide.dungeon.doorfinder.DungeonDoor;
import kr.syeyoung.dungeonsguide.roomprocessor.ProcessorFactory;
import kr.syeyoung.dungeonsguide.roomprocessor.RoomProcessor;
import kr.syeyoung.dungeonsguide.roomprocessor.RoomProcessorGenerator;
+import lombok.AllArgsConstructor;
import lombok.Getter;
+import lombok.Setter;
import net.minecraft.block.Block;
import net.minecraft.util.BlockPos;
@@ -36,6 +38,17 @@ public class DungeonRoom {
private final int unitWidth; // X
private final int unitHeight; // Z
+ @Setter
+ private int totalSecrets = -1;
+ @Setter
+ private RoomState currentState = RoomState.DISCOVERED;
+
+ @AllArgsConstructor
+ @Getter
+ public static enum RoomState {
+ DISCOVERED(0), COMPLETE_WITHOUT_SECRETS(0), FINISHED(0), FAILED(-14);
+ private int scoreModifier;
+ }
private RoomProcessor roomProcessor;
@@ -55,7 +68,6 @@ public class DungeonRoom {
unitWidth = (int) Math.ceil(max.getX() - min.getX() / 32.0);
unitHeight = (int) Math.ceil(max.getZ() - min.getZ() / 32.0);
-
buildDoors();
buildRoom();
updateRoomProcessor();
@@ -88,6 +100,7 @@ public class DungeonRoom {
dungeonRoomInfo = roomMatcher.createNew();
this.dungeonRoomInfo = dungeonRoomInfo;
+ totalSecrets = dungeonRoomInfo.getTotalSecrets();
}
public void updateRoomProcessor() {
diff --git a/src/main/java/kr/syeyoung/dungeonsguide/eventlistener/DungeonListener.java b/src/main/java/kr/syeyoung/dungeonsguide/eventlistener/DungeonListener.java
index 3651593a..7998c893 100755
--- a/src/main/java/kr/syeyoung/dungeonsguide/eventlistener/DungeonListener.java
+++ b/src/main/java/kr/syeyoung/dungeonsguide/eventlistener/DungeonListener.java
@@ -31,6 +31,7 @@ import net.minecraftforge.client.event.RenderWorldLastEvent;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.gameevent.InputEvent;
import net.minecraftforge.fml.common.gameevent.TickEvent;
+import org.lwjgl.opengl.GL11;
import java.awt.*;
@@ -80,12 +81,14 @@ public class DungeonListener {
SkyblockStatus skyblockStatus = (SkyblockStatus) e.getDungeonsGuide().getSkyblockStatus();
if (!skyblockStatus.isOnDungeon()) return;
if (FeatureRegistry.DEBUG.isEnabled()) {
+ GL11.glPushMatrix();
int[] textureData = dynamicTexture.getTextureData();
MapUtils.getImage().getRGB(0, 0, 128, 128, textureData, 0, 128);
dynamicTexture.updateDynamicTexture();
Minecraft.getMinecraft().getTextureManager().bindTexture(location);
GlStateManager.enableAlpha();
GuiScreen.drawModalRectWithCustomSizedTexture(0, 0, 0, 0, 128, 128, 128, 128);
+ GL11.glPopMatrix();
}
if (skyblockStatus.getContext() != null) {
@@ -100,9 +103,10 @@ public class DungeonListener {
fontRenderer.drawString("Where are you?!", 5, 128, 0xFFFFFF);
} else {
if (FeatureRegistry.DEBUG.isEnabled()) {
- fontRenderer.drawString("you're in the room... " + dungeonRoom.getColor() + " / " + dungeonRoom.getShape(), 5, 128, 0xFFFFFF);
+ fontRenderer.drawString("you're in the room... color/shape " + dungeonRoom.getColor() + " / " + dungeonRoom.getShape(), 5, 128, 0xFFFFFF);
fontRenderer.drawString("room uuid: " + dungeonRoom.getDungeonRoomInfo().getUuid() + (dungeonRoom.getDungeonRoomInfo().isRegistered() ? "" : " (not registered)"), 5, 138, 0xFFFFFF);
fontRenderer.drawString("room name: " + dungeonRoom.getDungeonRoomInfo().getName(), 5, 148, 0xFFFFFF);
+ fontRenderer.drawString("room state / max secret: " + dungeonRoom.getCurrentState() + " / "+dungeonRoom.getTotalSecrets(), 5, 158, 0xFFFFFF);
}
if (dungeonRoom.getRoomProcessor() != null) {
dungeonRoom.getRoomProcessor().drawScreen(postRender.partialTicks);
@@ -118,7 +122,6 @@ public class DungeonListener {
@SubscribeEvent
public void onChatReceived(ClientChatReceivedEvent clientChatReceivedEvent) {
try {
- if (clientChatReceivedEvent.type == 2) return;
SkyblockStatus skyblockStatus = (SkyblockStatus) e.getDungeonsGuide().getSkyblockStatus();
if (!skyblockStatus.isOnDungeon()) return;
@@ -133,13 +136,19 @@ public class DungeonListener {
DungeonRoom dungeonRoom = context.getRoomMapper().get(roomPt);
if (dungeonRoom != null) {
if (dungeonRoom.getRoomProcessor() != null) {
+ if (clientChatReceivedEvent.type == 2) {
+ dungeonRoom.getRoomProcessor().actionbarReceived(clientChatReceivedEvent.message);
+ roomProcessor = dungeonRoom.getRoomProcessor();
+ } else {
dungeonRoom.getRoomProcessor().chatReceived(clientChatReceivedEvent.message);
roomProcessor = dungeonRoom.getRoomProcessor();
+ }
}
}
} catch (Throwable t) {
t.printStackTrace();
}
+ if (clientChatReceivedEvent.type == 2) return;
for (RoomProcessor globalRoomProcessor : context.getGlobalRoomProcessors()) {
if (globalRoomProcessor == roomProcessor) continue;;
try {
diff --git a/src/main/java/kr/syeyoung/dungeonsguide/eventlistener/FeatureListener.java b/src/main/java/kr/syeyoung/dungeonsguide/eventlistener/FeatureListener.java
index 2e8e8adc..11531352 100644
--- a/src/main/java/kr/syeyoung/dungeonsguide/eventlistener/FeatureListener.java
+++ b/src/main/java/kr/syeyoung/dungeonsguide/eventlistener/FeatureListener.java
@@ -1,10 +1,12 @@
package kr.syeyoung.dungeonsguide.eventlistener;
import kr.syeyoung.dungeonsguide.SkyblockStatus;
+import kr.syeyoung.dungeonsguide.config.guiconfig.GuiGuiLocationConfig;
import kr.syeyoung.dungeonsguide.e;
import kr.syeyoung.dungeonsguide.features.*;
import kr.syeyoung.dungeonsguide.features.AbstractFeature;
import kr.syeyoung.dungeonsguide.features.listener.*;
+import net.minecraft.client.Minecraft;
import net.minecraftforge.client.event.*;
import net.minecraftforge.event.entity.player.ItemTooltipEvent;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
@@ -14,12 +16,14 @@ public class FeatureListener {
@SubscribeEvent
public void onRender(RenderGameOverlayEvent.Post postRender) {
try {
+ boolean isLocConfig = Minecraft.getMinecraft().currentScreen instanceof GuiGuiLocationConfig;
+
if (postRender.type != RenderGameOverlayEvent.ElementType.ALL) return;
SkyblockStatus skyblockStatus = e.getDungeonsGuide().getSkyblockStatus();
if (!skyblockStatus.isOnSkyblock()) return;
for (AbstractFeature abstractFeature : FeatureRegistry.getFeatureList()) {
- if (abstractFeature instanceof ScreenRenderListener) {
+ if (abstractFeature instanceof ScreenRenderListener && (!isLocConfig || !(abstractFeature instanceof GuiFeature))) {
((ScreenRenderListener) abstractFeature).drawScreen(postRender.partialTicks);
}
}
diff --git a/src/main/java/kr/syeyoung/dungeonsguide/features/FeatureRegistry.java b/src/main/java/kr/syeyoung/dungeonsguide/features/FeatureRegistry.java
index 6a66a13f..04e938ab 100644
--- a/src/main/java/kr/syeyoung/dungeonsguide/features/FeatureRegistry.java
+++ b/src/main/java/kr/syeyoung/dungeonsguide/features/FeatureRegistry.java
@@ -30,6 +30,9 @@ public class FeatureRegistry {
return abstractFeature;
}
+ public static final SimpleFeature DEBUG = register(new SimpleFeature("hidden", "Debug", "Toggles debug mode", "debug", false));
+
+ public static final SimpleFeature ADVANCED_ROOMEDIT = register(new SimpleFeature("advanced", "Room Edit", "Allow editing dungeon rooms\n\nWarning: using this feature can break or freeze your Minecraft\nThis is only for advanced users only", "advanced.roomedit", false));
public static final SimpleFeature SOLVER_RIDDLE = register(new SimpleFeature("solver", "Riddle Puzzle (3 weirdo) Solver", "Highlights the correct box after clicking on all 3 weirdos", "solver.riddle"));
public static final SimpleFeature SOLVER_KAHOOT = register(new SimpleFeature("solver", "Trivia Puzzle (Omnicrescent) Solver", "Highlights the correct solution for trivia puzzle", "solver.trivia"));
@@ -42,17 +45,21 @@ public class FeatureRegistry {
public static final SimpleFeature SOLVER_CREEPER = register(new SimpleFeature("solver", "Creeper Puzzle Solver", "Draws line between prismarine lamps in creeper room", "solver.creeper"));
public static final SimpleFeature SOLVER_TELEPORT = register(new SimpleFeature("solver", "Teleport Puzzle Solver", "Shows teleport pads you've visited in a teleport maze room", "solver.teleport"));
- public static final FeatureTooltipDungeonStat TOOLTIP_DUNGEONSTAT = register(new FeatureTooltipDungeonStat());
- public static final FeatureTooltipPrice TOOLTIP_PRICE = register(new FeatureTooltipPrice());
-
- public static final SimpleFeature ADVANCED_ROOMEDIT = register(new SimpleFeature("advanced", "Room Edit", "Allow editing dungeon rooms\n\nWarning: using this feature can break or freeze your Minecraft\nThis is only for advanced users only", "advanced.roomedit", false));
+ public static final FeatureTooltipDungeonStat ETC_DUNGEONSTAT = register(new FeatureTooltipDungeonStat());
+ public static final FeatureTooltipPrice ETC_PRICE = register(new FeatureTooltipPrice());
+ public static final FeatureCooldownCounter ETC_COOLDOWN = register(new FeatureCooldownCounter());
+ public static final SimpleFeature FIX_SPIRIT_BOOTS = register(new SimpleFeature("fixes", "Spirit Boots Fixer", "Fix Spirit boots messing up with inventory", "fixes.spirit", true));
- public static final SimpleFeature DEBUG = register(new SimpleFeature("hidden", "Debug", "Toggles debug mode", "debug", false));
+ public static final SimpleFeature BOSSFIGHT_CHESTPRICE = register(new FeatureChestPrice());
- public static final FeatureCooldownCounter HUD_COOLDOWN = register(new FeatureCooldownCounter());
public static final FeatureInstaCloseChest DUNGEON_INSTACLOSE = register(new FeatureInstaCloseChest());
public static final FeatureBoxSkelemaster DUNGEON_BOXSKELEMASTER = register(new FeatureBoxSkelemaster());
- public static final SimpleFeature FIX_SPIRIT_BOOTS = register(new SimpleFeature("fixes", "Spirit Boots Fixer", "Fix Spirit boots messing up with inventory", "fixes.spirit", true));
- public static final SimpleFeature BOSSFIGHT_CHESTPRICE = register(new FeatureChestPrice());
+ public static final FeatureDungeonDeaths DUNGEON_DEATHS = register(new FeatureDungeonDeaths());
+ public static final FeatureDungeonMilestone DUNGEON_MILESTONE = register(new FeatureDungeonMilestone());
+ public static final FeatureDungeonRealTime DUNGEON_REALTIME = register(new FeatureDungeonRealTime());
+ public static final FeatureDungeonSBTime DUNGEON_SBTIME = register(new FeatureDungeonSBTime());
+ public static final FeatureDungeonSecrets DUNGEON_SECRETS = register(new FeatureDungeonSecrets());
+ public static final FeatureDungeonTombs DUNGEON_TOMBS = register(new FeatureDungeonTombs());
+ public static final FeatureDungeonScore DUNGEON_SCORE = register(new FeatureDungeonScore());
}
diff --git a/src/main/java/kr/syeyoung/dungeonsguide/features/GuiFeature.java b/src/main/java/kr/syeyoung/dungeonsguide/features/GuiFeature.java
index d9d964c1..192ad10c 100644
--- a/src/main/java/kr/syeyoung/dungeonsguide/features/GuiFeature.java
+++ b/src/main/java/kr/syeyoung/dungeonsguide/features/GuiFeature.java
@@ -9,6 +9,7 @@ import lombok.Setter;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.FontRenderer;
import net.minecraft.client.gui.ScaledResolution;
+import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.OpenGlHelper;
import org.lwjgl.opengl.GL11;
@@ -39,21 +40,18 @@ public abstract class GuiFeature extends AbstractFeature implements ScreenRender
@Override
public void drawScreen(float partialTicks) {
if (!isEnabled()) return;
- GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS);
+ GL11.glPushMatrix();
+// GL11.glColor4f(1,1,1,1);
+ GlStateManager.disableFog();
+ GlStateManager.disableLighting();
clip(new ScaledResolution(Minecraft.getMinecraft()), featureRect.x, featureRect.y, featureRect.width, featureRect.height);
GL11.glEnable(GL11.GL_SCISSOR_TEST);
-// GL11.glEnable(GL11.GL_BLEND);
-// OpenGlHelper.glBlendFunc(770, 771, 1, 0);
-// GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
-
- GL11.glPushMatrix();
GL11.glTranslated(featureRect.x, featureRect.y, 0);
drawHUD(partialTicks);
- GL11.glPopMatrix();
GL11.glDisable(GL11.GL_SCISSOR_TEST);
- GL11.glPopAttrib();
+ GL11.glPopMatrix();
}
diff --git a/src/main/java/kr/syeyoung/dungeonsguide/features/impl/FeatureDungeonDeaths.java b/src/main/java/kr/syeyoung/dungeonsguide/features/impl/FeatureDungeonDeaths.java
new file mode 100644
index 00000000..ff3be74b
--- /dev/null
+++ b/src/main/java/kr/syeyoung/dungeonsguide/features/impl/FeatureDungeonDeaths.java
@@ -0,0 +1,96 @@
+package kr.syeyoung.dungeonsguide.features.impl;
+
+import kr.syeyoung.dungeonsguide.SkyblockStatus;
+import kr.syeyoung.dungeonsguide.dungeon.DungeonContext;
+import kr.syeyoung.dungeonsguide.e;
+import kr.syeyoung.dungeonsguide.features.FeatureParameter;
+import kr.syeyoung.dungeonsguide.features.GuiFeature;
+import kr.syeyoung.dungeonsguide.features.listener.ChatListener;
+import kr.syeyoung.dungeonsguide.features.listener.TickListener;
+import kr.syeyoung.dungeonsguide.utils.TextUtils;
+import net.minecraft.client.gui.FontRenderer;
+import net.minecraft.util.ChatComponentText;
+import net.minecraftforge.client.event.ClientChatReceivedEvent;
+import org.lwjgl.opengl.GL11;
+
+import java.awt.*;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class FeatureDungeonDeaths extends GuiFeature implements ChatListener {
+ public FeatureDungeonDeaths() {
+ super("Dungeon", "Display Deaths", "Display names of player and death count in dungeon run", "dungeon.stats.deaths", false, getFontRenderer().getStringWidth("longestplayernamepos: 100"), getFontRenderer().FONT_HEIGHT * 6);
+ this.setEnabled(false);
+ parameters.put("color", new FeatureParameter<Color>("color", "Color", "Color of text", Color.orange, "color"));
+ }
+
+ SkyblockStatus skyblockStatus = e.getDungeonsGuide().getSkyblockStatus();
+ @Override
+ public void drawHUD(float partialTicks) {
+ if (!skyblockStatus.isOnDungeon()) return;
+ DungeonContext context = skyblockStatus.getContext();
+ if (context == null) return;
+ Map<String, Integer> deaths = context.getDeaths();
+ int i = 0;
+ int deathsCnt = 0;
+ FontRenderer fr = getFontRenderer();
+ for (Map.Entry<String, Integer> death:deaths.entrySet()) {
+ fr.drawString(death.getKey()+": "+death.getValue(), 0,i, this.<Color>getParameter("color").getValue().getRGB());
+ i += 8;
+ deathsCnt += death.getValue();
+ }
+ fr.drawString("Total Deaths: "+deathsCnt, 0,i, this.<Color>getParameter("color").getValue().getRGB());
+ }
+
+ public int getTotalDeaths() {
+ if (!skyblockStatus.isOnDungeon()) return 0;
+ DungeonContext context = skyblockStatus.getContext();
+ if (context == null) return 0;
+ int d = 0;
+ for (Integer value : context.getDeaths().values()) {
+ d += value;
+ }
+ return d;
+ }
+
+ @Override
+ public void drawDemo(float partialTicks) {
+ FontRenderer fr = getFontRenderer();
+ fr.drawString("syeyoung: -130", 0,0, this.<Color>getParameter("color").getValue().getRGB());
+ fr.drawString("notsyeyoung: -13", 0,8, this.<Color>getParameter("color").getValue().getRGB());
+ fr.drawString("dungeonsguide: -42", 0,16, this.<Color>getParameter("color").getValue().getRGB());
+ fr.drawString("--not more--", 0,24, this.<Color>getParameter("color").getValue().getRGB());
+ fr.drawString("--maybe more--", 0,32, this.<Color>getParameter("color").getValue().getRGB());
+ fr.drawString("Total Deaths: 0", 0,40, this.<Color>getParameter("color").getValue().getRGB());
+ }
+
+ Pattern deathPattern = Pattern.compile("§r§c ☠ (.+?)§r§7 .+and became a ghost.+");
+ Pattern meDeathPattern = Pattern.compile("§r§c ☠ §r§7You .+and became a ghost.+");
+
+ @Override
+ public void onChat(ClientChatReceivedEvent clientChatReceivedEvent) {
+ if (clientChatReceivedEvent.type == 2) return;
+ System.out.println("HUH");
+ if (!skyblockStatus.isOnDungeon()) return;
+ DungeonContext context = skyblockStatus.getContext();
+ if (context == null) return;
+
+ String txt = clientChatReceivedEvent.message.getFormattedText();
+ System.out.println(txt);
+ Matcher m = deathPattern.matcher(txt);
+ if (m.matches()) {
+ String nickname = TextUtils.stripColor(m.group(1));
+ int deaths = context.getDeaths().containsKey(nickname) ? context.getDeaths().get(nickname) : 0;
+ context.getDeaths().put(nickname, deaths + 1);
+ e.sendDebugChat(new ChatComponentText("Death verified :: "+nickname+" / "+(deaths + 1)));
+ }
+ Matcher m2 = meDeathPattern.matcher(txt);
+ if (m2.matches()) {
+ String nickname = "me";
+ int deaths = context.getDeaths().containsKey(nickname) ? context.getDeaths().get(nickname) : 0;
+ context.getDeaths().put(nickname, deaths + 1);
+ e.sendDebugChat(new ChatComponentText("Death verified :: me / "+(deaths + 1)));
+ }
+ }
+}
diff --git a/src/main/java/kr/syeyoung/dungeonsguide/features/impl/FeatureDungeonMilestone.java b/src/main/java/kr/syeyoung/dungeonsguide/features/impl/FeatureDungeonMilestone.java
new file mode 100644
index 00000000..9ccd5e18
--- /dev/null
+++ b/src/main/java/kr/syeyoung/dungeonsguide/features/impl/FeatureDungeonMilestone.java
@@ -0,0 +1,74 @@
+package kr.syeyoung.dungeonsguide.features.impl;
+
+import kr.syeyoung.dungeonsguide.SkyblockStatus;
+import kr.syeyoung.dungeonsguide.dungeon.DungeonContext;
+import kr.syeyoung.dungeonsguide.e;
+import kr.syeyoung.dungeonsguide.features.FeatureParameter;
+import kr.syeyoung.dungeonsguide.features.FeatureRegistry;
+import kr.syeyoung.dungeonsguide.features.GuiFeature;
+import kr.syeyoung.dungeonsguide.features.listener.ChatListener;
+import kr.syeyoung.dungeonsguide.features.listener.TickListener;
+import kr.syeyoung.dungeonsguide.utils.TextUtils;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.FontRenderer;
+import net.minecraft.client.network.NetworkPlayerInfo;
+import net.minecraft.scoreboard.ScorePlayerTeam;
+import net.minecraft.util.ChatComponentText;
+import net.minecraftforge.client.event.ClientChatReceivedEvent;
+import org.lwjgl.opengl.GL11;
+
+import java.awt.*;
+import java.util.regex.Pattern;
+
+public class FeatureDungeonMilestone extends GuiFeature implements ChatListener {
+ public FeatureDungeonMilestone() {
+ super("Dungeon", "Display Current Class Milestone", "Display current class milestone of yourself", "dungeon.stats.milestone", true, getFontRenderer().getStringWidth("Milestone: 12"), getFontRenderer().FONT_HEIGHT);
+ this.setEnabled(false);
+ parameters.put("color", new FeatureParameter<Color>("color", "Color", "Color of text", Color.orange, "color"));
+ }
+
+ SkyblockStatus skyblockStatus = e.getDungeonsGuide().getSkyblockStatus();
+ @Override
+ public void drawHUD(float partialTicks) {
+ if (!skyblockStatus.isOnDungeon()) return;
+ FontRenderer fr = getFontRenderer();
+ double scale = getFeatureRect().getHeight() / fr.FONT_HEIGHT;
+ GL11.glScaled(scale, scale, 0);
+ for (NetworkPlayerInfo networkPlayerInfoIn : Minecraft.getMinecraft().thePlayer.sendQueue.getPlayerInfoMap()) {
+ String name = networkPlayerInfoIn.getDisplayName() != null ? networkPlayerInfoIn.getDisplayName().getFormattedText() : ScorePlayerTeam.formatPlayerName(networkPlayerInfoIn.getPlayerTeam(), networkPlayerInfoIn.getGameProfile().getName());
+ if (name.startsWith("§r Milestone: §r")) {
+ String milestone = TextUtils.stripColor(name).substring(13);
+ fr.drawString("Milestone: "+milestone, 0,0, this.<Color>getParameter("color").getValue().getRGB());
+ return;
+ }
+ }
+ fr.drawString("Milestone: ?", 0,0, this.<Color>getParameter("color").getValue().getRGB());
+ }
+
+ @Override
+ public void drawDemo(float partialTicks) {
+ FontRenderer fr = getFontRenderer();
+ double scale = getFeatureRect().getHeight() / fr.FONT_HEIGHT;
+ GL11.glScaled(scale, scale, 0);
+ fr.drawString("Milestone: 9", 0,0, this.<Color>getParameter("color").getValue().getRGB());
+ }
+ public static final Pattern milestone_pattern = Pattern.compile("§r§e§l(.+) Milestone §r§e(.)§r§7: .+ §r§a(.+)§r");
+
+
+ @Override
+ public void onChat(ClientChatReceivedEvent clientChatReceivedEvent) {
+ if (clientChatReceivedEvent.type == 2) return;;
+ if (!skyblockStatus.isOnDungeon()) return;
+ DungeonContext context = skyblockStatus.getContext();
+ if (context == null) return;
+ String txt = clientChatReceivedEvent.message.getFormattedText();
+ System.out.println(txt);
+ if (milestone_pattern.matcher(txt).matches()) {
+ context.getMilestoneReached().add(new String[] {
+ TextUtils.formatTime(FeatureRegistry.DUNGEON_REALTIME.getTimeElapsed()),
+ TextUtils.formatTime(FeatureRegistry.DUNGEON_SBTIME.getTimeElapsed())
+ });
+ e.sendDebugChat(new ChatComponentText("Reached Milestone At " + TextUtils.formatTime(FeatureRegistry.DUNGEON_REALTIME.getTimeElapsed()) + " / "+TextUtils.formatTime(FeatureRegistry.DUNGEON_SBTIME.getTimeElapsed())));
+ }
+ }
+}
diff --git a/src/main/java/kr/syeyoung/dungeonsguide/features/impl/FeatureDungeonRealTime.java b/src/main/java/kr/syeyoung/dungeonsguide/features/impl/FeatureDungeonRealTime.java
new file mode 100644
index 00000000..9b2620e4
--- /dev/null
+++ b/src/main/java/kr/syeyoung/dungeonsguide/features/impl/FeatureDungeonRealTime.java
@@ -0,0 +1,57 @@
+package kr.syeyoung.dungeonsguide.features.impl;
+
+import kr.syeyoung.dungeonsguide.SkyblockStatus;
+import kr.syeyoung.dungeonsguide.e;
+import kr.syeyoung.dungeonsguide.features.FeatureParameter;
+import kr.syeyoung.dungeonsguide.features.GuiFeature;
+import kr.syeyoung.dungeonsguide.features.listener.ChatListener;
+import kr.syeyoung.dungeonsguide.features.listener.TickListener;
+import kr.syeyoung.dungeonsguide.utils.TextUtils;
+import net.minecraft.client.gui.FontRenderer;
+import net.minecraftforge.client.event.ClientChatReceivedEvent;
+import org.lwjgl.opengl.GL11;
+
+import java.awt.*;
+import java.text.SimpleDateFormat;
+
+public class FeatureDungeonRealTime extends GuiFeature implements TickListener {
+ public FeatureDungeonRealTime() {
+ super("Dungeon", "Display Real Time-Dungeon Time", "Display how much real time has passed since dungeon run started", "dungeon.stats.realtime", true, getFontRenderer().getStringWidth("Time(Real): 59m 59s"), getFontRenderer().FONT_HEIGHT);
+ this.setEnabled(false);
+ parameters.put("color", new FeatureParameter<Color>("color", "Color", "Color of text", Color.orange, "color"));
+ }
+
+ private long started = -1;
+ @Override
+ public void drawHUD(float partialTicks) {
+ if (started == -1) return;
+ FontRenderer fr = getFontRenderer();
+ double scale = getFeatureRect().getHeight() / fr.FONT_HEIGHT;
+ GL11.glScaled(scale, scale, 0);
+ fr.drawString("Time(Real): "+TextUtils.formatTime(getTimeElapsed()), 0,0, this.<Color>getParameter("color").getValue().getRGB());
+ }
+
+ public long getTimeElapsed() {
+ return System.currentTimeMillis() - started;
+ }
+
+ @Override
+ public void drawDemo(float partialTicks) {
+ FontRenderer fr = getFontRenderer();
+ double scale = getFeatureRect().getHeight() / fr.FONT_HEIGHT;
+ GL11.glScaled(scale, scale, 0);
+ fr.drawString("Time(Real): -42h", 0,0, this.<Color>getParameter("color").getValue().getRGB());
+ }
+
+ SkyblockStatus skyblockStatus = e.getDungeonsGuide().getSkyblockStatus();
+ private boolean wasInDungeon = false;
+ @Override
+ public void onTick() {
+ if (wasInDungeon && !skyblockStatus.isOnDungeon()) {
+ if (skyblockStatus.isOnSkyblock()) started = -1;
+ } else if (!wasInDungeon && skyblockStatus.isOnDungeon()) {
+ started = System.currentTimeMillis();
+ }
+ wasInDungeon = skyblockStatus.isOnDungeon();
+ }
+}
diff --git a/src/main/java/kr/syeyoung/dungeonsguide/features/impl/FeatureDungeonSBTime.java b/src/main/java/kr/syeyoung/dungeonsguide/features/impl/FeatureDungeonSBTime.java
new file mode 100644
index 00000000..65f06f58
--- /dev/null
+++ b/src/main/java/kr/syeyoung/dungeonsguide/features/impl/FeatureDungeonSBTime.java
@@ -0,0 +1,92 @@
+package kr.syeyoung.dungeonsguide.features.impl;
+
+import kr.syeyoung.dungeonsguide.SkyblockStatus;
+import kr.syeyoung.dungeonsguide.e;
+import kr.syeyoung.dungeonsguide.features.FeatureParameter;
+import kr.syeyoung.dungeonsguide.features.GuiFeature;
+import kr.syeyoung.dungeonsguide.features.listener.ChatListener;
+import kr.syeyoung.dungeonsguide.features.listener.TickListener;
+import kr.syeyoung.dungeonsguide.utils.TextUtils;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.FontRenderer;
+import net.minecraft.scoreboard.Score;
+import net.minecraft.scoreboard.ScoreObjective;
+import net.minecraft.scoreboard.ScorePlayerTeam;
+import net.minecraft.scoreboard.Scoreboard;
+import net.minecraftforge.client.event.ClientChatReceivedEvent;
+import org.lwjgl.opengl.GL11;
+
+import java.awt.*;
+import java.util.Collection;
+
+public class FeatureDungeonSBTime extends GuiFeature implements TickListener {
+ public FeatureDungeonSBTime() {
+ super("Dungeon", "Display Ingame Dungeon Time", "Display how much time skyblock thinks has passed since dungeon run started", "dungeon.stats.igtime", true, getFontRenderer().getStringWidth("Time(IG): 1h 59m 59s"), getFontRenderer().FONT_HEIGHT);
+ this.setEnabled(false);
+ parameters.put("color", new FeatureParameter<Color>("color", "Color", "Color of text", Color.orange, "color"));
+ }
+
+ private long started = -1;
+ @Override
+ public void drawHUD(float partialTicks) {
+ if (started == -1) return;
+ FontRenderer fr = getFontRenderer();
+ double scale = getFeatureRect().getHeight() / fr.FONT_HEIGHT;
+ GL11.glScaled(scale, scale, 0);
+ Scoreboard scoreboard = Minecraft.getMinecraft().theWorld.getScoreboard();
+ ScoreObjective objective = scoreboard.getObjectiveInDisplaySlot(1);
+ Collection<Score> scores = scoreboard.getSortedScores(objective);
+ String time = "idkyet";
+ for (Score sc:scores) {
+ ScorePlayerTeam scorePlayerTeam = scoreboard.getPlayersTeam(sc.getPlayerName());
+ String strippedLine = TextUtils.keepScoreboardCharacters(TextUtils.stripColor(ScorePlayerTeam.formatPlayerName(scorePlayerTeam, sc.getPlayerName()))).trim();
+ if (strippedLine.startsWith("Time Elapsed: ")) {
+ time = strippedLine.substring(14);
+ }
+ }
+ fr.drawString("Time(Ig): "+time, 0,0, this.<Color>getParameter("color").getValue().getRGB());
+ }
+ public int getTimeElapsed() {
+ Scoreboard scoreboard = Minecraft.getMinecraft().theWorld.getScoreboard();
+ ScoreObjective objective = scoreboard.getObjectiveInDisplaySlot(1);
+ Collection<Score> scores = scoreboard.getSortedScores(objective);
+ String time = "idkyet";
+ for (Score sc:scores) {
+ ScorePlayerTeam scorePlayerTeam = scoreboard.getPlayersTeam(sc.getPlayerName());
+ String strippedLine = TextUtils.keepScoreboardCharacters(TextUtils.stripColor(ScorePlayerTeam.formatPlayerName(scorePlayerTeam, sc.getPlayerName()))).trim();
+ if (strippedLine.startsWith("Time Elapsed: ")) {
+ time = strippedLine.substring(14);
+ }
+ }
+ time = time.replace(" ", "");
+ int hour = time.indexOf('h') == -1 ? 0 : Integer.parseInt(time.substring(0, time.indexOf('h')));
+ if (time.contains("h")) time = time.substring(time.indexOf('h') + 1);
+ int minute = time.indexOf('m') == -1 ? 0 : Integer.parseInt(time.substring(0, time.indexOf('m')));
+ if (time.contains("m")) time = time.substring(time.indexOf('m') + 1);
+ int second = time.indexOf('s') == -1 ? 0 : Integer.parseInt(time.substring(0, time.indexOf('s')));
+
+ int time2 = hour * 60 * 60 + minute * 60 + second;
+ return time2 * 1000;
+ }
+
+ @Override
+ public void drawDemo(float partialTicks) {
+ FontRenderer fr = getFontRenderer();
+ double scale = getFeatureRect().getHeight() / fr.FONT_HEIGHT;
+ GL11.glScaled(scale, scale, 0);
+
+ fr.drawString("Time(Ig): -42h", 0,0, this.<Color>getParameter("color").getValue().getRGB());
+ }
+
+ SkyblockStatus skyblockStatus = e.getDungeonsGuide().getSkyblockStatus();
+ private boolean wasInDungeon = false;
+ @Override
+ public void onTick() {
+ if (wasInDungeon && !skyblockStatus.isOnDungeon()) {
+ if (skyblockStatus.isOnSkyblock()) started = -1;
+ } else if (!wasInDungeon && skyblockStatus.isOnDungeon()) {
+ started = System.currentTimeMillis();
+ }
+ wasInDungeon = skyblockStatus.isOnDungeon();
+ }
+}
diff --git a/src/main/java/kr/syeyoung/dungeonsguide/features/impl/FeatureDungeonScore.java b/src/main/java/kr/syeyoung/dungeonsguide/features/impl/FeatureDungeonScore.java
new file mode 100644
index 00000000..024ccb67
--- /dev/null
+++ b/src/main/java/kr/syeyoung/dungeonsguide/features/impl/FeatureDungeonScore.java
@@ -0,0 +1,167 @@
+package kr.syeyoung.dungeonsguide.features.impl;
+
+import kr.syeyoung.dungeonsguide.SkyblockStatus;
+import kr.syeyoung.dungeonsguide.dungeon.DungeonContext;
+import kr.syeyoung.dungeonsguide.dungeon.roomfinder.DungeonRoom;
+import kr.syeyoung.dungeonsguide.e;
+import kr.syeyoung.dungeonsguide.features.FeatureParameter;
+import kr.syeyoung.dungeonsguide.features.FeatureRegistry;
+import kr.syeyoung.dungeonsguide.features.GuiFeature;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import net.minecraft.client.gui.FontRenderer;
+import net.minecraft.util.MathHelper;
+import org.lwjgl.opengl.GL11;
+
+import java.awt.*;
+
+public class FeatureDungeonScore extends GuiFeature {
+ public FeatureDungeonScore() {
+ super("Dungeon", "Display Current Score", "Calculate and Display current score\nThis data is from pure calculation and can be different from actual score.", "dungeon.stats.score", false, 200, getFontRenderer().FONT_HEIGHT * 4);
+ this.setEnabled(false);
+ parameters.put("color", new FeatureParameter<Color>("color", "Color", "Color of text", Color.orange, "color"));
+ parameters.put("verbose", new FeatureParameter<Boolean>("verbose", "Show each score instead of sum", "Skill: 100 Explore: 58 S->S+(5 tombs) instead of Score: 305", true, "boolean"));
+ }
+
+ SkyblockStatus skyblockStatus = e.getDungeonsGuide().getSkyblockStatus();
+ @Override
+ public void drawHUD(float partialTicks) {
+ if (!skyblockStatus.isOnDungeon()) return;
+ FontRenderer fr = getFontRenderer();
+ ScoreCalculation score = calculateScore();
+ int sum = score.time + score.skill + score.explorer + score.bonus;
+ if (this.<Boolean>getParameter("verbose").getValue()) {
+ String req = buildRequirement(score);
+ int rgb = this.<Color>getParameter("color").getValue().getRGB();
+ fr.drawString("Skill: "+score.skill+" ("+score.deaths+" Deaths)", 0, 0, rgb);
+ fr.drawString("Explorer: "+score.explorer+" (Rooms "+(score.fullyCleared ? "O" : "X") + " secrets "+score.secrets+"/"+score.totalSecrets+(score.totalSecretsKnown ? "": "?")+")", 0, 8, rgb);
+ fr.drawString("Time: "+score.time+" Bonus: "+score.bonus+" ::: Total: "+sum, 0, 16, rgb);
+ fr.drawString(req, 0, 24, rgb);
+ } else {
+ double scale = getFeatureRect().getHeight() / fr.FONT_HEIGHT;
+ GL11.glScaled(scale, scale, 0);
+ String letter = getLetter(sum);
+ fr.drawString("Score: "+score + "("+letter+")", 0,0, this.<Color>getParameter("color").getValue().getRGB());
+ }
+ }
+
+ @Override
+ public void drawDemo(float partialTicks) {
+ FontRenderer fr = getFontRenderer();
+ if (this.<Boolean>getParameter("verbose").getValue()) {
+ int rgb = this.<Color>getParameter("color").getValue().getRGB();
+ fr.drawString("Skill: 100 (0 Deaths)", 0, 0, rgb);
+ fr.drawString("Explorer: 99 (Rooms O 39/40)", 0, 8, rgb);
+ fr.drawString("Time: 100 Bonus: 0 Total: 299", 0, 16, rgb);
+ fr.drawString("S->S+ (1 Required 1 tomb)", 0, 24, rgb);
+ } else {
+ double scale = getFeatureRect().getHeight() / fr.FONT_HEIGHT;
+ GL11.glScaled(scale, scale, 0);
+ fr.drawString("Score: 305 (S+)", 0,0, this.<Color>getParameter("color").getValue().getRGB());
+ }
+ }
+
+ @Data
+ @AllArgsConstructor
+ public static class ScoreCalculation {
+ private int skill, explorer, time, bonus, tombs;
+ private boolean fullyCleared;
+ private int secrets, totalSecrets;
+ private boolean totalSecretsKnown;
+ private int deaths;
+ }
+
+ public ScoreCalculation calculateScore() {
+ if (!skyblockStatus.isOnDungeon()) return null;
+ DungeonContext context = skyblockStatus.getContext();
+ if (context == null) return null;
+ int skill = 100;
+ int deaths = 0;
+ {
+ deaths = FeatureRegistry.DUNGEON_DEATHS.getTotalDeaths();
+ skill -= FeatureRegistry.DUNGEON_DEATHS.getTotalDeaths() * 2;
+ for (DungeonRoom dungeonRoom : context.getDungeonRoomList()) {
+ skill += dungeonRoom.getCurrentState().getScoreModifier();
+ }
+ }
+ int explorer = 0;
+ boolean fullyCleared = false;
+ boolean totalSecretsKnown = true;
+ int totalSecrets = 0;
+ int secrets = 0;
+ {
+ int clearedRooms = 0;
+ for (DungeonRoom dungeonRoom : context.getDungeonRoomList()) {
+ if (!(dungeonRoom.getCurrentState() == DungeonRoom.RoomState.DISCOVERED)) {
+ clearedRooms ++;
+ }
+ if (dungeonRoom.getTotalSecrets() != -1)
+ totalSecrets += dungeonRoom.getTotalSecrets();
+ else totalSecretsKnown = false;
+ }
+ fullyCleared = clearedRooms == context.getDungeonRoomList().size() && context.getMapProcessor().getUndiscoveredRoom() == 0;
+ explorer += MathHelper.clamp_int((int) Math.floor(60 * (clearedRooms / ((double)context.getDungeonRoomList().size() + context.getMapProcessor().getUndiscoveredRoom()))), 0, 60);
+ explorer += MathHelper.clamp_int((int) Math.floor(40 * ((secrets = FeatureRegistry.DUNGEON_SECRETS.getSecretsFound()) / (double)totalSecrets)),0,40);
+ }
+ int time = 0;
+ {
+ double timeModifier;
+ if (context.getMapProcessor().isInBossRoom()) {
+ timeModifier = Math.max(0, context.getBossRoomEnterSeconds() - 1200);
+ } else {
+ timeModifier = Math.max(0, FeatureRegistry.DUNGEON_SBTIME.getTimeElapsed() / 1000 - 1200);
+ }
+ time = (int) Math.floor(100 - 2.2 * timeModifier);
+ }
+ int bonus = 0;
+ int tombs;
+ {
+ bonus += tombs = MathHelper.clamp_int(FeatureRegistry.DUNGEON_TOMBS.getTombsFound(), 0, 5);
+ }
+
+ // amazing thing
+ return new ScoreCalculation(skill, explorer, time, bonus, tombs, fullyCleared, secrets, totalSecrets, totalSecretsKnown, deaths);
+ }
+ public String getLetter(int score) {
+ if (score <= 99) return "D";
+ if (score <= 159) return "C";
+ if (score <= 229) return "B";
+ if (score <= 269) return "A";
+ if (score <= 299) return "S";
+ return "S+";
+ }
+ public int getScoreRequirement(String letter) {
+ if (letter.equals("D")) return 0;
+ if (letter.equals("C")) return 100;
+ if (letter.equals("B")) return 160;
+ if (letter.equals("A")) return 230;
+ if (letter.equals("S")) return 270;
+ if (letter.equals("S+")) return 300;
+ return -1;
+ }
+ public String getNextLetter(String letter) {
+ if (letter.equals("D")) return "C";
+ if (letter.equals("C")) return "B";
+ if (letter.equals("B")) return "A";
+ if (letter.equals("A")) return "S";
+ if (letter.equals("S")) return "S+";
+ else return null;
+ }
+ public String buildRequirement(ScoreCalculation calculation) {
+ int current = calculation.time + calculation.bonus + calculation.explorer + calculation.skill;
+ String currentLetter = getLetter(current);
+ String nextLetter= getNextLetter(currentLetter);
+ if (nextLetter == null) return "S+ Expected";
+ int req = getScoreRequirement(nextLetter);
+ int reqPT2 = req- current;
+ int reqPT = req - current;
+
+ int tombsBreakable = Math.min(5 - calculation.tombs, reqPT);
+ reqPT -= tombsBreakable;
+
+ double secretPer = 40.0 / calculation.totalSecrets;
+ int secrets = (int) Math.ceil(reqPT / secretPer);
+
+ return currentLetter+"->"+nextLetter+" ("+reqPT2+" Req "+tombsBreakable+" crypts "+secrets+" secrets"+(calculation.totalSecretsKnown ? "" : "?")+")";
+ }
+}
diff --git a/src/main/java/kr/syeyoung/dungeonsguide/features/impl/FeatureDungeonSecrets.java b/src/main/java/kr/syeyoung/dungeonsguide/features/impl/FeatureDungeonSecrets.java
new file mode 100644
index 00000000..afe8da07
--- /dev/null
+++ b/src/main/java/kr/syeyoung/dungeonsguide/features/impl/FeatureDungeonSecrets.java
@@ -0,0 +1,71 @@
+package kr.syeyoung.dungeonsguide.features.impl;
+
+import kr.syeyoung.dungeonsguide.SkyblockStatus;
+import kr.syeyoung.dungeonsguide.dungeon.DungeonContext;
+import kr.syeyoung.dungeonsguide.dungeon.roomfinder.DungeonRoom;
+import kr.syeyoung.dungeonsguide.e;
+import kr.syeyoung.dungeonsguide.features.FeatureParameter;
+import kr.syeyoung.dungeonsguide.features.FeatureRegistry;
+import kr.syeyoung.dungeonsguide.features.GuiFeature;
+import kr.syeyoung.dungeonsguide.features.listener.ChatListener;
+import kr.syeyoung.dungeonsguide.features.listener.TickListener;
+import kr.syeyoung.dungeonsguide.utils.TextUtils;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.FontRenderer;
+import net.minecraft.client.network.NetworkPlayerInfo;
+import net.minecraft.scoreboard.ScorePlayerTeam;
+import net.minecraft.util.ChatComponentText;
+import net.minecraftforge.client.event.ClientChatReceivedEvent;
+import org.lwjgl.opengl.GL11;
+
+import java.awt.*;
+import java.util.regex.Pattern;
+
+public class FeatureDungeonSecrets extends GuiFeature {
+ public FeatureDungeonSecrets() {
+ super("Dungeon", "Display #Secrets", "Display how much total secrets have been found in a dungeon run.\n+ sign means DG does not know the correct number, but it's somewhere above that number.", "dungeon.stats.secrets", true, getFontRenderer().getStringWidth("Secrets: 999/999+"), getFontRenderer().FONT_HEIGHT);
+ this.setEnabled(false);
+ parameters.put("color", new FeatureParameter<Color>("color", "Color", "Color of text", Color.orange, "color"));
+ }
+
+ SkyblockStatus skyblockStatus = e.getDungeonsGuide().getSkyblockStatus();
+ @Override
+ public void drawHUD(float partialTicks) {
+ if (!skyblockStatus.isOnDungeon()) return;
+ FontRenderer fr = getFontRenderer();
+ double scale = getFeatureRect().getHeight() / fr.FONT_HEIGHT;
+ GL11.glScaled(scale, scale, 0);
+ fr.drawString("Secrets: "+getSecretsFound() +"/"+getTotalSecrets(), 0,0, this.<Color>getParameter("color").getValue().getRGB());
+ }
+
+ @Override
+ public void drawDemo(float partialTicks) {
+ FontRenderer fr = getFontRenderer();
+ double scale = getFeatureRect().getHeight() / fr.FONT_HEIGHT;
+ GL11.glScaled(scale, scale, 0);
+ fr.drawString("Secrets: 999/2+", 0,0, this.<Color>getParameter("color").getValue().getRGB());
+ }
+
+ public int getSecretsFound() {
+ for (NetworkPlayerInfo networkPlayerInfoIn : Minecraft.getMinecraft().thePlayer.sendQueue.getPlayerInfoMap()) {
+ String name = networkPlayerInfoIn.getDisplayName() != null ? networkPlayerInfoIn.getDisplayName().getFormattedText() : ScorePlayerTeam.formatPlayerName(networkPlayerInfoIn.getPlayerTeam(), networkPlayerInfoIn.getGameProfile().getName());
+ if (name.startsWith("§r Secrets Found: §r§b")) {
+ String noColor = TextUtils.stripColor(name);
+ return Integer.parseInt(noColor.substring(16));
+ }
+ }
+ return 0;
+ }
+
+ public String getTotalSecrets() {
+ DungeonContext context = skyblockStatus.getContext();
+ int totalSecrets = 0;
+ boolean allknown = true;
+ for (DungeonRoom dungeonRoom : context.getDungeonRoomList()) {
+ if (dungeonRoom.getTotalSecrets() != -1)
+ totalSecrets += dungeonRoom.getTotalSecrets();
+ else allknown = false;
+ }
+ return totalSecrets + (allknown ? "":"+");
+ }
+}
diff --git a/src/main/java/kr/syeyoung/dungeonsguide/features/impl/FeatureDungeonTombs.java b/src/main/java/kr/syeyoung/dungeonsguide/features/impl/FeatureDungeonTombs.java
new file mode 100644
index 00000000..511e971f
--- /dev/null
+++ b/src/main/java/kr/syeyoung/dungeonsguide/features/impl/FeatureDungeonTombs.java
@@ -0,0 +1,50 @@
+package kr.syeyoung.dungeonsguide.features.impl;
+
+import kr.syeyoung.dungeonsguide.SkyblockStatus;
+import kr.syeyoung.dungeonsguide.e;
+import kr.syeyoung.dungeonsguide.features.FeatureParameter;
+import kr.syeyoung.dungeonsguide.features.GuiFeature;
+import kr.syeyoung.dungeonsguide.utils.TextUtils;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.FontRenderer;
+import net.minecraft.client.network.NetworkPlayerInfo;
+import net.minecraft.scoreboard.ScorePlayerTeam;
+import org.lwjgl.opengl.GL11;
+
+import java.awt.*;
+
+public class FeatureDungeonTombs extends GuiFeature {
+ public FeatureDungeonTombs() {
+ super("Dungeon", "Display #Crypts", "Display how much total crypts have been blown up in a dungeon run", "dungeon.stats.tombs", true, getFontRenderer().getStringWidth("Crypts: 42"), getFontRenderer().FONT_HEIGHT);
+ this.setEnabled(false);
+ parameters.put("color", new FeatureParameter<Color>("color", "Color", "Color of text", Color.orange, "color"));
+ }
+
+ SkyblockStatus skyblockStatus = e.getDungeonsGuide().getSkyblockStatus();
+ @Override
+ public void drawHUD(float partialTicks) {
+ if (!skyblockStatus.isOnDungeon()) return;
+ FontRenderer fr = getFontRenderer();
+ double scale = getFeatureRect().getHeight() / fr.FONT_HEIGHT;
+ GL11.glScaled(scale, scale, 0);
+ fr.drawString("Crypts: "+getTombsFound(), 0,0, this.<Color>getParameter("color").getValue().getRGB());
+ }
+
+ @Override
+ public void drawDemo(float partialTicks) {
+ FontRenderer fr = getFontRenderer();
+ double scale = getFeatureRect().getHeight() / fr.FONT_HEIGHT;
+ GL11.glScaled(scale, scale, 0);
+ fr.drawString("Crypts: 42", 0,0, this.<Color>getParameter("color").getValue().getRGB());
+ }
+
+ public int getTombsFound() {
+ for (NetworkPlayerInfo networkPlayerInfoIn : Minecraft.getMinecraft().thePlayer.sendQueue.getPlayerInfoMap()) {
+ String name = networkPlayerInfoIn.getDisplayName() != null ? networkPlayerInfoIn.getDisplayName().getFormattedText() : ScorePlayerTeam.formatPlayerName(networkPlayerInfoIn.getPlayerTeam(), networkPlayerInfoIn.getGameProfile().getName());
+ if (name.startsWith("§r Crypts: §r§6")) {
+ return Integer.parseInt(TextUtils.stripColor(name).substring(9));
+ }
+ }
+ return 0;
+ }
+}
diff --git a/src/main/java/kr/syeyoung/dungeonsguide/roomprocessor/GeneralRoomProcessor.java b/src/main/java/kr/syeyoung/dungeonsguide/roomprocessor/GeneralRoomProcessor.java
index 541d5ccc..a46ea8cb 100755
--- a/src/main/java/kr/syeyoung/dungeonsguide/roomprocessor/GeneralRoomProcessor.java
+++ b/src/main/java/kr/syeyoung/dungeonsguide/roomprocessor/GeneralRoomProcessor.java
@@ -1,13 +1,17 @@
package kr.syeyoung.dungeonsguide.roomprocessor;
import kr.syeyoung.dungeonsguide.config.Config;
+import kr.syeyoung.dungeonsguide.dungeon.DungeonContext;
import kr.syeyoung.dungeonsguide.dungeon.mechanics.DungeonMechanic;
import kr.syeyoung.dungeonsguide.dungeon.roomfinder.DungeonRoom;
+import kr.syeyoung.dungeonsguide.e;
import kr.syeyoung.dungeonsguide.features.FeatureRegistry;
import kr.syeyoung.dungeonsguide.roomedit.EditingContext;
import kr.syeyoung.dungeonsguide.roomedit.gui.GuiDungeonRoomEdit;
import lombok.Getter;
import lombok.Setter;
+import net.minecraft.client.Minecraft;
+import net.minecraft.util.BlockPos;
import net.minecraft.util.IChatComponent;
import java.awt.*;
@@ -48,6 +52,35 @@ public class GeneralRoomProcessor implements RoomProcessor {
}
@Override
+ public void actionbarReceived(IChatComponent chat) {
+ if (dungeonRoom.getTotalSecrets() != -1) return;
+ BlockPos pos = Minecraft.getMinecraft().thePlayer.getPosition();
+ DungeonContext context = e.getDungeonsGuide().getSkyblockStatus().getContext();
+ Point pt1 = context.getMapProcessor().worldPointToRoomPoint(pos.add(2,0,2));
+ Point pt2 = context.getMapProcessor().worldPointToRoomPoint(pos.add(-2,0,-2));
+ if (!pt1.equals(pt2)) {
+ return;
+ }
+
+
+ String text = chat.getFormattedText();
+ int secretsIndex = text.indexOf("Secrets");
+ if (secretsIndex == -1) {
+ dungeonRoom.setTotalSecrets(0);
+ return;
+ }
+ int theindex = 0;
+ for (int i = secretsIndex; i > 0; i--) {
+ if (text.startsWith("§7", i)) {
+ theindex = i;
+ }
+ }
+ String it = text.substring(theindex + 2, secretsIndex- 1);
+ int maxSecret = Integer.parseInt(it.split("/")[1]);
+ dungeonRoom.setTotalSecrets(maxSecret);
+ }
+
+ @Override
public boolean readGlobalChat() {
return false;
}
diff --git a/src/main/java/kr/syeyoung/dungeonsguide/roomprocessor/RoomProcessor.java b/src/main/java/kr/syeyoung/dungeonsguide/roomprocessor/RoomProcessor.java
index f768ac40..e922cfd1 100755
--- a/src/main/java/kr/syeyoung/dungeonsguide/roomprocessor/RoomProcessor.java
+++ b/src/main/java/kr/syeyoung/dungeonsguide/roomprocessor/RoomProcessor.java
@@ -8,6 +8,7 @@ public interface RoomProcessor {
void drawScreen(float partialTicks);
void drawWorld(float partialTicks);
void chatReceived(IChatComponent chat);
+ void actionbarReceived(IChatComponent chat);
boolean readGlobalChat();
} \ No newline at end of file
diff --git a/src/main/java/kr/syeyoung/dungeonsguide/utils/TextUtils.java b/src/main/java/kr/syeyoung/dungeonsguide/utils/TextUtils.java
index 300cf6ba..a27a1695 100755
--- a/src/main/java/kr/syeyoung/dungeonsguide/utils/TextUtils.java
+++ b/src/main/java/kr/syeyoung/dungeonsguide/utils/TextUtils.java
@@ -62,4 +62,23 @@ public class TextUtils {
return hasDecimal ? ((truncated / 10.0D) + suffix) : ((truncated / 10L) + suffix);
}
+ public static String formatTime(long ms) {
+ long seconds = ms / 1000;
+ long hr = seconds / (60 * 60); seconds -= hr * 60 * 60;
+ long min = seconds / 60; seconds -= min * 60;
+
+ StringBuilder stringBuilder = new StringBuilder();
+ if (hr > 0) {
+ stringBuilder.append(hr).append("h ");
+ }
+ if (hr > 0 || min > 0) {
+ stringBuilder.append(min).append("m ");
+ }
+ if (hr > 0 || min > 0 || seconds > 0) {
+ stringBuilder.append(seconds).append("s ");
+ }
+
+ return stringBuilder.toString();
+ }
+
} \ No newline at end of file