Timerv2, PlayerVis Hotkey, small other changes

This commit is contained in:
Stachelbeere1248 2024-06-24 00:28:01 +02:00
parent 70cd4eb724
commit 5c882edfb0
Signed by: Stachelbeere1248
SSH key fingerprint: SHA256:IozEKdw2dB8TZxkpPdMxcWSoWTIMwoLaCcZJ1AJnY2o
32 changed files with 344 additions and 364 deletions

View file

@ -4,8 +4,8 @@ Hello, I am currently working on this mod. More features will come. For now it h
- An accurate timer + Automatic splitting
- Tracking of splits & segment PBs (with custom categories)
- SLA hud
- 1 chat macro
- Spawntime (no aa colors yet, but Rocket Launcher mode)
- A chat macro
- Spawn-times HUD (no aa colors yet, but Rocket Launcher mode)
#### Disclaimers
- If you are using a Hypixel language other than the selected one the mod may not work entirely. Check config.
## For Users
@ -25,7 +25,7 @@ The timer automatically splits every round. The PB/Segment recorder automaticall
- Enabled: Whether the SLA HUD should automatically be shown when starting a game.
- Truncate: Whether inactive windows and rooms should be shown.
- Macro Message: The Message to be sent when pressing the Chat Macro Key. Do NOT use "§" as symbol.
- Player Visibility: Whether to show players even if they are within a 4 block radius or not.
- Player Visibility: Whether to enable PlayerVisibility by default.
- CPS Counter: A simple CPS Counter which shows the amount of clicks within the last 20 gameticks.
### Commands
- /category \<name> - Switches to the category called name. All recorded times are bound to its category. Tabcomplete suggests already existing categories, but you can insert a new (clean) one as well.
@ -46,7 +46,8 @@ The timer automatically splits every round. The PB/Segment recorder automaticall
- /zombiesutils timer split \<round> - Splits as if \<round> was passed, not recommended to use as it might create impossible PBs.
- /qz \<de|bb|aa|p> - sends you to a new game of Dead End, Bad Blood, Alien Arcadium or Prison
### Hotkeys
- Chat Macro: sends the message specified in the config
- RL Mode: Toggle usage of the rocket launcher mode spawn-time offset.
- Chat Macro: Sends the message specified in the config.
- RL Mode: Toggles usage of the rocket launcher mode spawn-time offset.
- Player Visibility: Toggles whether to show players that are within a 4 block radius.
### Extra
- Managing split-categories: In your game directory is a folder called "zombies" which contains the folder "splits". You can simply rename or delete the folders inside "splits". You can also edit your splits, the data is stored as a list of ticks inside the MAP_DIFFICULTY.times files, a simple text editor (such as Notepad on Windows) should be able to edit it (UTF-16 encoded text). The other subfolder, runs, logs all the splits for every run you play.

View file

@ -1,2 +1 @@
- Distinguishing Escape
- Multi-Timer support

View file

@ -3,4 +3,4 @@ org.gradle.jvmargs=-Xmx2g
baseGroup = com.github.stachelbeere1248.zombiesutils
mcVersion = 1.8.9
modid = zombiesutils
version = 1.3.0-PREVIEW_4
version = 1.3.1-PREVIEW_4

View file

@ -4,6 +4,7 @@ import com.github.stachelbeere1248.zombiesutils.commands.CommandRegistry;
import com.github.stachelbeere1248.zombiesutils.config.Hotkeys;
import com.github.stachelbeere1248.zombiesutils.config.ZombiesUtilsConfig;
import com.github.stachelbeere1248.zombiesutils.handlers.Handlers;
import com.github.stachelbeere1248.zombiesutils.timer.GameManager;
import net.minecraft.client.Minecraft;
import net.minecraftforge.common.config.Configuration;
import net.minecraftforge.fml.common.Mod;
@ -16,12 +17,15 @@ import org.jetbrains.annotations.NotNull;
public class ZombiesUtils {
private static ZombiesUtils instance;
private final Hotkeys hotkeys;
private final GameManager gameManager;
private ZombiesUtilsConfig config;
private Handlers handlers;
private Logger logger;
public ZombiesUtils() {
hotkeys = new Hotkeys();
gameManager = new GameManager();
instance = this;
}
@ -66,4 +70,8 @@ public class ZombiesUtils {
public ZombiesUtilsConfig getConfig() {
return config;
}
public GameManager getGameManager() {
return gameManager;
}
}

View file

@ -1,6 +1,6 @@
package com.github.stachelbeere1248.zombiesutils.commands;
import com.github.stachelbeere1248.zombiesutils.timer.Timer;
import com.github.stachelbeere1248.zombiesutils.ZombiesUtils;
import com.github.stachelbeere1248.zombiesutils.timer.recorder.Category;
import net.minecraft.command.CommandException;
import net.minecraft.command.ICommand;
@ -42,8 +42,8 @@ public class CategoryCommand implements ICommand {
if (cat.contains(File.separator))
throw new WrongUsageException("Your name must not contain '" + File.separator + "' as this is the systems separator character for folder" + File.separator + "sub-folder");
Category.setSelectedCategory(cat);
ZombiesUtils.getInstance().getGameManager().getGame().ifPresent(game -> game.setCategory(new Category()));
sender.addChatMessage(new ChatComponentText("§eSet category to §c" + cat));
Timer.getInstance().ifPresent(timer -> timer.setCategory(new Category()));
}
}

View file

@ -119,7 +119,7 @@ public class SlaCommand extends CommandBase {
List<String> options = new ArrayList<>();
if (args.length == 1) options.addAll(Arrays.asList("off", "offset", "rotate", "mirror", "map", "quick"));
else {
if (args.length > 1) switch (args[0]) {
if (args.length >= 2) switch (args[0]) {
case "offset":
if (args.length < 5) options.add("0");
break;

View file

@ -1,6 +1,9 @@
package com.github.stachelbeere1248.zombiesutils.commands;
import com.github.stachelbeere1248.zombiesutils.timer.Timer;
import com.github.stachelbeere1248.zombiesutils.ZombiesUtils;
import com.github.stachelbeere1248.zombiesutils.utils.InvalidMapException;
import com.github.stachelbeere1248.zombiesutils.utils.ScoardboardException;
import com.github.stachelbeere1248.zombiesutils.utils.Scoreboard;
import net.minecraft.command.*;
import net.minecraft.util.BlockPos;
import org.jetbrains.annotations.NotNull;
@ -29,18 +32,24 @@ public class ZombiesUtilsCommand extends CommandBase {
case "timer":
switch (args[1]) {
case "kill":
Timer.dropInstances();
String serverNumber = Scoreboard.getServerNumber().orElse("");
if (args.length == 3) serverNumber = args[2];
ZombiesUtils.getInstance().getGameManager().endGame(serverNumber, false);
break;
case "killall":
ZombiesUtils.getInstance().getGameManager().killAll();
case "split":
try {
Timer.getInstance().ifPresent(timer -> timer.split(Byte.parseByte(args[2])));
ZombiesUtils.getInstance().getGameManager().splitOrNew(Integer.parseInt(args[2]));
} catch (NumberFormatException | NullPointerException ignored) {
throw new NumberInvalidException("t", args[2]);
} catch (ScoardboardException | InvalidMapException e) {
ZombiesUtils.getInstance().getLogger().error(e.getStackTrace());
}
break;
default:
throw new WrongUsageException(
"[Invalid option] options: kill, split", args[0]);
"[Invalid option] options: kill, killall, split", args[0]);
}
break;
default:
@ -52,13 +61,26 @@ public class ZombiesUtilsCommand extends CommandBase {
@Override
public List<String> addTabCompletionOptions(ICommandSender sender, String @NotNull [] args, BlockPos blockPos) {
if (args.length == 1) return new ArrayList<>(Collections.singleton("timer"));
else if (args.length == 2) {
switch (args[0]) {
case "timer":
return new ArrayList<>(Arrays.asList("kill", "split"));
return new ArrayList<>(Arrays.asList("kill", "killall", "split"));
default:
return Collections.emptyList();
}
}
else if (args.length == 3) {
switch (args[0]) {
case "timer":
switch (args[1]) {
case "kill":
return new ArrayList<>(ZombiesUtils.getInstance().getGameManager().getGames());
}
default:
return Collections.emptyList();
}
} else return Collections.emptyList();
}
@Override
public boolean canCommandSenderUseCommand(ICommandSender sender) {

View file

@ -7,6 +7,7 @@ import org.lwjgl.input.Keyboard;
public class Hotkeys {
private final KeyBinding chatMacro;
private final KeyBinding rlSpawn;
private final KeyBinding playerVisiblity;
public Hotkeys() {
chatMacro = new KeyBinding(
@ -16,7 +17,12 @@ public class Hotkeys {
);
rlSpawn = new KeyBinding(
"Rocket Launcher Mode",
Keyboard.KEY_NONE,
Keyboard.KEY_UP,
"Zombies Utils"
);
playerVisiblity = new KeyBinding(
"Player Visibility",
Keyboard.KEY_RIGHT,
"Zombies Utils"
);
@ -25,6 +31,7 @@ public class Hotkeys {
public void registerAll() {
ClientRegistry.registerKeyBinding(this.chatMacro);
ClientRegistry.registerKeyBinding(this.rlSpawn);
ClientRegistry.registerKeyBinding(this.playerVisiblity);
}
public KeyBinding getChatMacro() {
@ -34,4 +41,7 @@ public class Hotkeys {
public KeyBinding getRlSpawn() {
return rlSpawn;
}
public KeyBinding getPlayerVisiblity() {
return playerVisiblity;
}
}

View file

@ -147,15 +147,15 @@ public class ZombiesUtilsConfig {
private List<IConfigElement> getSlaElements() {
return Arrays.asList(
new CustomConfigElement("Enabled", slaToggle),
new CustomConfigElement("Truncate", slaShortener),
new CustomConfigElement("PB announcements", announcePB)
new CustomConfigElement("Truncate", slaShortener)
);
}
private List<IConfigElement> getTimerElements() {
return Arrays.asList(
new CustomConfigElement("Default category", defaultCategory),
new CustomConfigElement("Paste delta", copyDelta)
new CustomConfigElement("Paste delta", copyDelta),
new CustomConfigElement("PB announcements", announcePB)
);
}

View file

@ -17,14 +17,12 @@ public enum Map {
BlockPos pos = new BlockPos(44,71,0);
if (!world.isBlockLoaded(pos) || Scoreboard.isNotZombies()) return Optional.empty();
Block block = world.getBlockState(pos).getBlock();
if (block.equals(Blocks.air)) {
return Optional.of(Map.DEAD_END);
} else if (block.equals(Blocks.wool)) {
return Optional.of(Map.BAD_BLOOD);
} else if (block.equals(Blocks.stone_slab)) {
return Optional.of(Map.ALIEN_ARCADIUM);
} else if (block.equals(Blocks.wooden_slab)) {
return Optional.of(Map.PRISON);
} else return Optional.empty();
if (block.equals(Blocks.air)) return Optional.of(Map.DEAD_END);
if (block.equals(Blocks.wool)) return Optional.of(Map.BAD_BLOOD);
if (block.equals(Blocks.stone_slab)) return Optional.of(Map.ALIEN_ARCADIUM);
if (block.equals(Blocks.wooden_slab)) return Optional.of(Map.PRISON);
else return Optional.empty();
}
}

View file

@ -1,7 +1,6 @@
package com.github.stachelbeere1248.zombiesutils.game.waves;
import com.github.stachelbeere1248.zombiesutils.ZombiesUtils;
import com.github.stachelbeere1248.zombiesutils.timer.Timer;
import com.github.stachelbeere1248.zombiesutils.utils.Scoreboard;
import net.minecraft.client.Minecraft;
import org.jetbrains.annotations.NotNull;
@ -10,27 +9,12 @@ import java.util.Arrays;
public class WaveTiming {
public static int rl = 0;
public static byte[] getWaves(@NotNull Timer timer) {
return Waves.get(
timer.getGameMode().getMap(),
timer.getRound()
);
}
public static byte getLastWave(@NotNull Timer timer) {
return Waves.getLastWave(
timer.getGameMode().getMap(),
timer.getRound()
);
}
public static void onTick() {
if (Scoreboard.isNotZombies()) return;
//TODO: Assert correct server number!
Timer.getInstance().ifPresent(timer -> {
byte[] waves = getWaves(timer);
final int roundTime = timer.roundTime();
ZombiesUtils.getInstance().getGameManager().getGame().ifPresent(
game -> {
byte[] waves = Waves.get(game.getGameMode().getMap(), game.getRound());
final int roundTime = game.getTimer().getRoundTime();
final int[] auditory = ZombiesUtils.getInstance().getConfig().getAuditory();
for (int wave : waves) {
wave = wave * 20 + rl;
@ -39,7 +23,8 @@ public class WaveTiming {
Minecraft.getMinecraft().thePlayer.playSound("note.pling", 1, 2);
}
}
});
}
);
}
public static void toggleRL() {

View file

@ -15,7 +15,7 @@ public class Waves {
prisonWaveTimes = {{10, 20}, {10, 20, 30}, {10, 17, 24, 31}, {10, 17, 24, 31}, {10, 20, 30}, {10, 20, 30}, {10, 20, 30}, {10, 25, 40}, {10, 25, 35}, {10, 25, 45}, {10, 25, 40}, {10, 25, 37}, {10, 22, 34}, {10, 25, 37}, {10, 25, 40}, {10, 22, 37}, {10, 22, 42}, {10, 25, 45}, {10, 25, 45}, {10, 25, 40}, {10, 20, 35, 55, 75}, {10, 25, 40}, {10, 30, 50}, {10, 30, 50}, {10, 25, 45}, {10, 30, 50}, {10, 25, 45}, {10, 30, 50}, {10, 30, 55}, {10}};
@Contract(pure = true)
public static byte[] get(@NotNull Map map, byte round) {
public static byte[] get(@NotNull Map map, int round) {
byte[] ret = new byte[]{0};
try {
switch (map) {
@ -40,7 +40,7 @@ public class Waves {
return ret;
}
public static byte getLastWave(@NotNull Map map, byte round) {
public static byte getLastWave(@NotNull Map map, int round) {
byte[] aByte = get(map, round);
return aByte[aByte.length - 1];
}

View file

@ -1,8 +1,7 @@
package com.github.stachelbeere1248.zombiesutils.handlers;
import com.github.stachelbeere1248.zombiesutils.game.GameMode;
import com.github.stachelbeere1248.zombiesutils.ZombiesUtils;
import com.github.stachelbeere1248.zombiesutils.game.enums.Difficulty;
import com.github.stachelbeere1248.zombiesutils.timer.Timer;
import com.github.stachelbeere1248.zombiesutils.utils.LanguageSupport;
import net.minecraftforge.client.event.ClientChatReceivedEvent;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
@ -17,16 +16,17 @@ public class ChatHandler {
}
@SubscribeEvent
public void difficultyChange(@NotNull ClientChatReceivedEvent event) {
if (!Timer.getInstance().isPresent()) return;
public void difficultyChange(@NotNull final ClientChatReceivedEvent event) {
ZombiesUtils.getInstance().getGameManager().getGame().ifPresent(
game -> {
String message = STRIP_COLOR_PATTERN.matcher(event.message.getUnformattedText()).replaceAll("").trim();
GameMode gameMode = Timer.getInstance().get().getGameMode();
if (message.contains(":")) return;
if (LanguageSupport.containsHard(message)) {
gameMode.changeDifficulty(Difficulty.HARD);
game.getGameMode().changeDifficulty(Difficulty.HARD);
} else if (LanguageSupport.containsRIP(message)) {
gameMode.changeDifficulty(Difficulty.RIP);
game.getGameMode().changeDifficulty(Difficulty.RIP);
}
}
);
}
}

View file

@ -5,9 +5,11 @@ import net.minecraftforge.common.MinecraftForge;
public class Handlers {
private final RenderGameOverlayHandler renderer;
private final RenderPlayerHandler renderPlayerHandler;
public Handlers() {
renderer = new RenderGameOverlayHandler();
renderPlayerHandler = new RenderPlayerHandler();
}
public void registerAll() {
@ -22,4 +24,8 @@ public class Handlers {
public RenderGameOverlayHandler getRenderer() {
return renderer;
}
public RenderPlayerHandler getRenderPlayerHandler() {
return renderPlayerHandler;
}
}

View file

@ -18,13 +18,13 @@ public class KeyInputHandler {
if (Keyboard.getEventKey() == '\0') return;
if (Keyboard.getEventKeyState()) {
Hotkeys hotkeys = ZombiesUtils.getInstance().getHotkeys();
if (Keyboard.getEventKey() == hotkeys.getChatMacro().getKeyCode()) {
Minecraft.getMinecraft().thePlayer.sendChatMessage(
ZombiesUtils.getInstance().getConfig().getChatMacro()
);
} else if (Keyboard.getEventKey() == hotkeys.getRlSpawn().getKeyCode()) {
if (Keyboard.getEventKey() == hotkeys.getChatMacro().getKeyCode()) Minecraft.getMinecraft().thePlayer
.sendChatMessage(ZombiesUtils.getInstance().getConfig().getChatMacro());
else if (Keyboard.getEventKey() == hotkeys.getRlSpawn().getKeyCode()) {
ZombiesUtils.getInstance().getHandlers().getRenderer().toggleRL();
WaveTiming.toggleRL();
} else if (Keyboard.getEventKey() == hotkeys.getPlayerVisiblity().getKeyCode()) {
ZombiesUtils.getInstance().getHandlers().getRenderPlayerHandler().togglePlayerVisibility();
}
}
} else if (event instanceof InputEvent.MouseInputEvent) {

View file

@ -4,7 +4,6 @@ import com.github.stachelbeere1248.zombiesutils.ZombiesUtils;
import com.github.stachelbeere1248.zombiesutils.game.SLA;
import com.github.stachelbeere1248.zombiesutils.game.waves.Waves;
import com.github.stachelbeere1248.zombiesutils.game.windows.Room;
import com.github.stachelbeere1248.zombiesutils.timer.Timer;
import com.github.stachelbeere1248.zombiesutils.utils.Scoreboard;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.FontRenderer;
@ -48,23 +47,25 @@ public class RenderGameOverlayHandler {
public void onRenderGameOverlay(RenderGameOverlayEvent.@NotNull Post event) {
if (event.type != RenderGameOverlayEvent.ElementType.TEXT) return;
Timer.getInstance().ifPresent(timer -> {
renderTime(timer.roundTime());
ZombiesUtils.getInstance().getGameManager().getGame().ifPresent(
game -> {
renderTime(game.getTimer().getRoundTime());
renderSpawnTime(
Waves.get(
timer.getGameMode().getMap(),
timer.getRound()
game.getGameMode().getMap(),
game.getRound()
),
timer.roundTime()
game.getTimer().getRoundTime()
);
}
);
});
SLA.getInstance().ifPresent(sla -> renderSla(sla.getRooms()));
if (ZombiesUtils.getInstance().getConfig().getCpsToggle()) renderCPS();
}
private void renderTime(long timerTicks) {
private void renderTime(short timerTicks) {
if (Scoreboard.isNotZombies()) return;
final String time = getTimeString(timerTicks);

View file

@ -1,6 +1,7 @@
package com.github.stachelbeere1248.zombiesutils.handlers;
import com.github.stachelbeere1248.zombiesutils.ZombiesUtils;
import com.github.stachelbeere1248.zombiesutils.config.ZombiesUtilsConfig;
import net.minecraft.client.Minecraft;
import net.minecraft.util.Vec3;
import net.minecraftforge.client.event.RenderPlayerEvent;
@ -8,15 +9,19 @@ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import org.jetbrains.annotations.NotNull;
public class RenderPlayerHandler {
private boolean visible = ZombiesUtils.getInstance().getConfig().getPlayerVis();
@SubscribeEvent
public void onRender(RenderPlayerEvent.@NotNull Pre event) {
if (event.entityPlayer.isPlayerSleeping() || event.entityPlayer.isUser()) return;
if (inRange(event.entityPlayer.getPositionVector())) {
event.setCanceled(!ZombiesUtils.getInstance().getConfig().getPlayerVis());
event.setCanceled(!visible);
}
}
private boolean inRange(Vec3 playerOther) {
private boolean inRange(@NotNull Vec3 playerOther) {
return playerOther.squareDistanceTo(Minecraft.getMinecraft().thePlayer.getPositionVector()) <= 16;
}
public void togglePlayerVisibility() {
this.visible = !this.visible;
}
}

View file

@ -10,11 +10,17 @@ import org.jetbrains.annotations.NotNull;
public class Round1Correction {
private final Timer TIMER;
public Round1Correction(Timer timer) {
TIMER = timer;
}
@SubscribeEvent
public void onWaveSpawn(@NotNull EntityJoinWorldEvent event) {
final Entity entity = event.entity;
if (entity instanceof EntityZombie) {
Timer.getInstance().ifPresent(Timer::correctRn);
TIMER.correctStartTick();
MinecraftForge.EVENT_BUS.unregister(this);
}
}

View file

@ -1,9 +1,9 @@
package com.github.stachelbeere1248.zombiesutils.mixin;
import com.github.stachelbeere1248.zombiesutils.ZombiesUtils;
import com.github.stachelbeere1248.zombiesutils.game.enums.Map;
import com.github.stachelbeere1248.zombiesutils.timer.Timer;
import com.github.stachelbeere1248.zombiesutils.utils.InvalidMapException;
import com.github.stachelbeere1248.zombiesutils.utils.LanguageSupport;
import com.github.stachelbeere1248.zombiesutils.utils.ScoardboardException;
import com.github.stachelbeere1248.zombiesutils.utils.Scoreboard;
import net.minecraft.client.Minecraft;
import net.minecraft.client.network.NetHandlerPlayClient;
@ -36,74 +36,32 @@ public class MixinNetHandlerPlayClient {
private void zombies_utils$handleSound(@NotNull S29PacketSoundEffect packet) {
if (Scoreboard.isNotZombies()) return;
final String soundEffect = packet.getSoundName();
if (!(
soundEffect.equals("mob.wither.spawn")
|| (soundEffect.equals("mob.guardian.curse") && !zombies_utils$alienUfoOpened)
|| (soundEffect.equals("mob.guardian.curse")
&& !zombies_utils$alienUfoOpened)
)) return;
zombies_utils$alienUfoOpened = soundEffect.equals("mob.guardian.curse");
try {
if (!Timer.getInstance().isPresent()) {
Timer.instance = new Timer(
Scoreboard.getServerNumber().orElseThrow(Timer.TimerException.ServerNumberException::new),
Map.getMap().orElseThrow(Timer.TimerException.MapException::new),
Scoreboard.getRound()
);
return;
}
final Timer running = Timer.getInstance().get();
final byte round = Scoreboard.getRound();
if (round == 0) {
if (Scoreboard.getLineCount() < 13) Timer.instance = new Timer(
Scoreboard.getServerNumber().orElseThrow(Timer.TimerException.ServerNumberException::new),
Map.getMap().orElseThrow(Timer.TimerException.MapException::new),
round
);
return;
}
if (!running.equalsServerOrNull(Scoreboard.getServerNumber().orElse(null))) {
Timer.instance = new Timer(
Scoreboard.getServerNumber().orElseThrow(Timer.TimerException.ServerNumberException::new),
Map.getMap().orElseThrow(Timer.TimerException.MapException::new),
round
);
return;
}
running.split(round);
} catch (Timer.TimerException e) {
ZombiesUtils.getInstance().getGameManager().splitOrNew(Scoreboard.getRound());
} catch (ScoardboardException | InvalidMapException e) {
Minecraft.getMinecraft().thePlayer.addChatMessage(new ChatComponentText("§cFailed to start or split timer. Please send a log to Stachelbeere1248."));
ZombiesUtils.getInstance().getLogger().warn(e);
ZombiesUtils.getInstance().getLogger().error(e.getStackTrace());
}
}
@Unique
private void zombies_utils$handleTitle(@NotNull S45PacketTitle packet) {
if (packet.getType() != S45PacketTitle.Type.TITLE) return;
final String message = packet.getMessage().getUnformattedText().trim();
Timer.getInstance().ifPresent(timer -> {
if (Scoreboard.isNotZombies()) return;
if (LanguageSupport.isWin(message)) {
switch (timer.getGameMode().getMap()) {
case DEAD_END:
case BAD_BLOOD:
case PRISON: //TODO: Escape
timer.split((byte) 30);
Timer.dropInstances();
break;
case ALIEN_ARCADIUM:
timer.split((byte) 105);
Timer.dropInstances();
break;
}
} else if (LanguageSupport.isLoss(message)) Timer.dropInstances();
});
final String message = packet.getMessage().getUnformattedText().trim();
String serverNumber;
serverNumber = Scoreboard.getServerNumber().orElse("");
if (LanguageSupport.isWin(message)) ZombiesUtils.getInstance().getGameManager().endGame(serverNumber,true);
if (LanguageSupport.isLoss(message)) ZombiesUtils.getInstance().getGameManager().endGame(serverNumber, false);
}
}

View file

@ -1,24 +1,98 @@
package com.github.stachelbeere1248.zombiesutils.timer;
import com.github.stachelbeere1248.zombiesutils.ZombiesUtils;
import com.github.stachelbeere1248.zombiesutils.game.GameMode;
import com.github.stachelbeere1248.zombiesutils.game.SLA;
import com.github.stachelbeere1248.zombiesutils.game.enums.Map;
import com.github.stachelbeere1248.zombiesutils.handlers.Round1Correction;
import com.github.stachelbeere1248.zombiesutils.timer.recorder.Category;
import com.github.stachelbeere1248.zombiesutils.timer.recorder.files.CategoryFile;
import com.github.stachelbeere1248.zombiesutils.timer.recorder.files.GameFile;
import net.minecraft.client.Minecraft;
import net.minecraft.event.ClickEvent;
import net.minecraft.util.ChatComponentText;
import net.minecraft.util.ChatStyle;
import net.minecraftforge.common.MinecraftForge;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
public class RecordManager {
public class Game {
private final Timer TIMER;
private final GameMode GAME_MODE;
private final GameFile GAME_FILE;
private final boolean roundOneRecorded;
private int round;
private Category category;
public Game(@NotNull final Map map, final String serverNumber) {
this.GAME_MODE = new GameMode(map);
this.TIMER = new Timer();
this.GAME_FILE = new GameFile(serverNumber, map);
this.category = new Category();
this.round = 1;
this.roundOneRecorded = true;
MinecraftForge.EVENT_BUS.register(new Round1Correction(TIMER));
if (ZombiesUtils.getInstance().getConfig().isSlaToggled()) SLA.instance = new SLA(map);
}
public Game(@NotNull final Map map, final String serverNumber, final int round) {
this.GAME_MODE = new GameMode(map);
this.TIMER = new Timer();
this.GAME_FILE = new GameFile(serverNumber, map);
this.category = new Category();
this.round = round;
this.roundOneRecorded = (round == 1);
MinecraftForge.EVENT_BUS.register(new Round1Correction(TIMER));
if (ZombiesUtils.getInstance().getConfig().isSlaToggled()) SLA.instance = new SLA(map);
}
public Timer getTimer() {
return this.TIMER;
};
public void setCategory(Category category) {
this.category = category;
}
public int getRound() {
return round;
}
public GameMode getGameMode() {
return GAME_MODE;
}
public void pass(int round) {
if ((round == 0) || (this.round == round + 1) || (this.TIMER.getRoundTime() < 100)) {
ZombiesUtils.getInstance().getLogger().debug("SPLIT CANCELLED");
return;
}
try {
record();
} catch (Exception e) {
ZombiesUtils.getInstance().getLogger().error(ExceptionUtils.getStackTrace(e));
Minecraft.getMinecraft().thePlayer.addChatMessage(new ChatComponentText("Error recording splits"));
}
this.TIMER.split();
this.round = round + 1;
}
private void record() {
this.compareSegment();
if (this.roundOneRecorded) this.compareBest();
this.GAME_FILE.setSegment(this.round, this.TIMER.getRoundTime());
}
private static final String bar = "§l§a▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬";
public static void compareSegment(byte round, short roundTime, @NotNull Category category) throws IndexOutOfBoundsException {
@SuppressWarnings("OptionalGetWithoutIsPresent") final CategoryFile categoryFile = category.getByGameMode(Timer.getInstance().get().getGameMode());
public void compareSegment() throws IndexOutOfBoundsException {
final CategoryFile categoryFile = this.category.getByGameMode(this.GAME_MODE);
final short bestSegment = categoryFile.getBestSegment(round);
final int roundTime = this.getTimer().getRoundTime();
final String timeString = formattedTime(roundTime);
String segmentMessage = bar + "\n§e Category: §d" + category.getName();
String segmentMessage = Game.bar + "\n§e Category: §d" + category.getName();
String deltaString = "";
@ -40,7 +114,7 @@ public class RecordManager {
}
segmentMessage += "\n" + bar;
segmentMessage += "\n" + Game.bar;
final ChatComponentText message = new ChatComponentText(segmentMessage);
String copyString = String.format("Round %s took %s%s!", round, timeString, deltaString);
@ -49,12 +123,13 @@ public class RecordManager {
Minecraft.getMinecraft().thePlayer.addChatMessage(message);
}
public static void compareBest(byte round, int gameTime, @NotNull Category category) throws IndexOutOfBoundsException {
@SuppressWarnings("OptionalGetWithoutIsPresent") final CategoryFile categoryFile = category.getByGameMode(Timer.getInstance().get().getGameMode());
public void compareBest() throws IndexOutOfBoundsException {
final CategoryFile categoryFile = this.category.getByGameMode(this.GAME_MODE);
final int personalBest = categoryFile.getPersonalBest(round);
final int gameTime = this.TIMER.getGameTime();
String deltaString = "";
String bestMessage = bar + "\n§e Category: §d" + category.getName();
String bestMessage = Game.bar + "\n§e Category: §d" + category.getName();
final String timeString = formattedTime(gameTime);
if (personalBest == 0) {
@ -73,7 +148,7 @@ public class RecordManager {
bestMessage += "\n§cRound " + round + "§e finished at §a" + timeString + " §9" + deltaString;
if (ZombiesUtils.getInstance().getConfig().getCopyDelta()) deltaString = " (" + deltaString + ")";
}
bestMessage += "\n" + bar;
bestMessage += "\n" + Game.bar;
final ChatComponentText message = new ChatComponentText(bestMessage);
String copyString = String.format("Round %s finished at %s%s!", round, timeString, deltaString);
@ -83,7 +158,7 @@ public class RecordManager {
}
@Contract(pure = true)
private static String formattedTime(int gameTime) {
private String formattedTime(int gameTime) {
gameTime *= 50;
return String.format("%d:%02d.%d%d",
gameTime / 60000,

View file

@ -0,0 +1,61 @@
package com.github.stachelbeere1248.zombiesutils.timer;
import com.github.stachelbeere1248.zombiesutils.game.enums.Map;
import com.github.stachelbeere1248.zombiesutils.utils.InvalidMapException;
import com.github.stachelbeere1248.zombiesutils.utils.ScoardboardException;
import com.github.stachelbeere1248.zombiesutils.utils.Scoreboard;
import java.util.HashMap;
import java.util.List;
import java.util.Optional;
import java.util.Set;
public class GameManager {
private final HashMap<String, Game> GAMES;
public GameManager() {
GAMES = new HashMap<>();
}
public Optional<Game> getGame() {
return Scoreboard.getServerNumber().map(GAMES::get);
}
public void endGame(final String serverNumber, final boolean isWin) {
if (!GAMES.containsKey(serverNumber)) return;
final Game game = GAMES.get(serverNumber);
if (isWin) {
switch (game.getGameMode().getMap()) {
case DEAD_END:
case BAD_BLOOD:
case PRISON: //TODO: Escape
game.pass((byte) 30);
break;
case ALIEN_ARCADIUM:
game.pass((byte) 105);
break;
}
}
GAMES.remove(serverNumber);
}
public void splitOrNew(int round) throws ScoardboardException, InvalidMapException {
final String serverNumber = Scoreboard.getServerNumber().orElseThrow(ScoardboardException::new);
if (GAMES.containsKey(serverNumber)) {
if (round == 0) GAMES.put(serverNumber, new Game(Map.getMap().orElseThrow(InvalidMapException::new), serverNumber));
else GAMES.get(serverNumber).pass(round);
} else {
GAMES.put(serverNumber, new Game(Map.getMap().orElseThrow(InvalidMapException::new), serverNumber, round + 1));
}
}
public Set<String> getGames() {
return this.GAMES.keySet();
}
public void killAll() {
GAMES.clear();
}
}

View file

@ -1,151 +1,33 @@
package com.github.stachelbeere1248.zombiesutils.timer;
import com.github.stachelbeere1248.zombiesutils.ZombiesUtils;
import com.github.stachelbeere1248.zombiesutils.game.GameMode;
import com.github.stachelbeere1248.zombiesutils.game.SLA;
import com.github.stachelbeere1248.zombiesutils.game.enums.Map;
import com.github.stachelbeere1248.zombiesutils.handlers.Round1Correction;
import com.github.stachelbeere1248.zombiesutils.timer.recorder.Category;
import com.github.stachelbeere1248.zombiesutils.timer.recorder.files.GameFile;
import net.minecraft.client.Minecraft;
import net.minecraft.util.ChatComponentText;
import net.minecraftforge.common.MinecraftForge;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.jetbrains.annotations.NotNull;
import java.util.Optional;
public class Timer {
public static Timer instance;
private final GameMode gameMode;
private final String serverNumber;
private final GameFile gameFile;
public Category category;
private long savedTotalWorldTime;
private int passedRoundsTickSum = 0;
private boolean pbTracking = false;
private int round;
private boolean r1Corrected = false;
private long startTick;
private int roundStart;
/**
* @param serverNumber The game's server the timer should be bound to.
* @param map The map the timer should be started for.
* @param round If available, round to begin splitting.
*/
public Timer(@NotNull String serverNumber, @NotNull Map map, byte round) throws TimerException.ServerNumberException {
this.savedTotalWorldTime = getCurrentTotalWorldTime();
if (!serverNumber.trim().isEmpty()) this.serverNumber = serverNumber.trim();
else throw new Timer.TimerException.ServerNumberException();
this.category = new Category();
this.gameFile = new GameFile(serverNumber.trim(), map);
this.gameMode = new GameMode(map);
this.round = round;
if (ZombiesUtils.getInstance().getConfig().isSlaToggled()) SLA.instance = new SLA(map);
MinecraftForge.EVENT_BUS.register(new Round1Correction());
public Timer() {
this.startTick = this.getCurrentTick();
}
public static Optional<Timer> getInstance() {
return Optional.ofNullable(instance);
public void correctStartTick() {
this.startTick = this.getCurrentTick() - 200;
}
void split() {
this.roundStart = this.getGameTime();
}
public int getGameTime() {
return (int) (getCurrentTick() - startTick);
}
/**
* Call to invalidate {@link #instance} to trigger the garbage collector
*/
public static void dropInstances() {
instance = null;
public short getRoundTime() {
return (short) (getGameTime() - roundStart);
}
/**
* The main splitting function.
* Cancels on the second occurring sound-effect, important for {@link RecordManager} to not override values incorrectly.
*
* @param passedRound The round that has been passed.
*/
public void split(byte passedRound) {
final int gameTime = gameTime();
final short roundTime = (short) (gameTime - passedRoundsTickSum);
if ((round == passedRound) || (passedRound == 0) || (roundTime < 100)) {
ZombiesUtils.getInstance().getLogger().debug("SPLIT CANCELLED");
return;
}
try {
record(passedRound, roundTime, gameTime);
} catch (Exception e) {
ZombiesUtils.getInstance().getLogger().error(ExceptionUtils.getStackTrace(e));
Minecraft.getMinecraft().thePlayer.addChatMessage(new ChatComponentText("Error saving splits"));
}
passedRoundsTickSum = gameTime;
round = passedRound;
}
public void correctRn() {
if (r1Corrected) return;
savedTotalWorldTime = getCurrentTotalWorldTime() - 200L;
r1Corrected = true;
}
private void record(byte passedRound, short roundTime, int gameTime) {
if (passedRound == (byte) 1) pbTracking = true;
try {
RecordManager.compareSegment(passedRound, roundTime, category);
if (pbTracking) RecordManager.compareBest(passedRound, gameTime, category);
gameFile.setSegment(passedRound, roundTime);
} catch (IndexOutOfBoundsException exception) {
Minecraft.getMinecraft().thePlayer.addChatMessage(new ChatComponentText(
String.format("Split not recorded. (invalid round parsed from scoreboard: %s)", passedRound)
));
}
}
private long getCurrentTotalWorldTime() {
if (Minecraft.getMinecraft() == null) return 0;
if (Minecraft.getMinecraft().theWorld == null) return 0;
private long getCurrentTick() {
if (Minecraft.getMinecraft() == null || Minecraft.getMinecraft().theWorld == null) return 0;
return Minecraft.getMinecraft().theWorld.getTotalWorldTime();
}
public int gameTime() {
return (int) (getCurrentTotalWorldTime() - savedTotalWorldTime);
}
public short roundTime() {
return (short) (gameTime() - passedRoundsTickSum);
}
/**
* @param serverNumber Servernumber to be compared
* @return false, if and only if input exists and is unequal to {@link #serverNumber}
*/
public boolean equalsServerOrNull(String serverNumber) {
return (serverNumber == null || serverNumber.equals(this.serverNumber) || serverNumber.isEmpty());
}
public void setCategory(Category category) {
this.category = category;
}
public byte getRound() {
return (byte) (round + 1);
}
public GameMode getGameMode() {
return gameMode;
}
public static abstract class TimerException extends Exception {
public static class MapException extends TimerException {
}
public static class ServerNumberException extends TimerException {
}
}
}

View file

@ -4,7 +4,6 @@ import com.github.stachelbeere1248.zombiesutils.ZombiesUtils;
import com.github.stachelbeere1248.zombiesutils.game.GameMode;
import com.github.stachelbeere1248.zombiesutils.game.enums.Difficulty;
import com.github.stachelbeere1248.zombiesutils.game.enums.Map;
import com.github.stachelbeere1248.zombiesutils.timer.Timer;
import com.github.stachelbeere1248.zombiesutils.timer.recorder.files.CategoryFile;
import org.jetbrains.annotations.NotNull;
@ -38,7 +37,7 @@ public class Category {
public static void setSelectedCategory(String selectedCategory) {
Category.selectedCategory = selectedCategory;
Timer.getInstance().ifPresent(timer -> timer.setCategory(new Category()));
ZombiesUtils.getInstance().getGameManager().getGame().ifPresent(game -> game.setCategory(new Category()));
}
public static String[] getCategories() {

View file

@ -28,7 +28,7 @@ public class FileManager {
return gson.fromJson(dataJson, CategoryData.class);
}
public static void createDataFile(@NotNull SplitsFile file, ISplitsData data) {
public static void createDataFile(@NotNull File file, ISplitsData data) {
try {
//noinspection ResultOfMethodCallIgnored
file.getParentFile().mkdirs();
@ -40,7 +40,7 @@ public class FileManager {
}
}
public static void writeDataToFile(SplitsFile file, @NotNull ISplitsData data) throws IOException {
public static void writeDataToFile(File file, @NotNull ISplitsData data) throws IOException {
FileUtils.writeStringToFile(file, data.toJSON(), StandardCharsets.UTF_16);
}

View file

@ -1,16 +0,0 @@
package com.github.stachelbeere1248.zombiesutils.timer.recorder;
import org.jetbrains.annotations.NotNull;
import java.io.File;
public abstract class SplitsFile extends File {
public SplitsFile(String parent, @NotNull String child) {
super(parent, child);
}
public SplitsFile(File category, String child) {
super(category, child);
}
}

View file

@ -19,10 +19,13 @@ public class CategoryData implements ISplitsData {
break;
case DEAD_END:
case BAD_BLOOD:
case PRISON:
bestSegments = new short[30];
personalBests = new int[30];
break;
case PRISON:
bestSegments = new short[31];
personalBests = new int[31];
break;
default:
throw new IllegalStateException("Not a map: " + map);
}
@ -45,8 +48,8 @@ public class CategoryData implements ISplitsData {
return personalBests[index];
}
public void setBestSegment(int index, short ticks) {
bestSegments[index] = ticks;
public void setBestSegment(int index, int ticks) {
bestSegments[index] = (short) ticks;
}
public void setPersonalBest(int index, int ticks) {

View file

@ -17,9 +17,11 @@ public class GameData implements ISplitsData {
break;
case DEAD_END:
case BAD_BLOOD:
case PRISON:
segments = new short[30];
break;
case PRISON:
segments = new short[31];
break;
default:
throw new IllegalStateException("Not a map: " + map);
}
@ -33,7 +35,7 @@ public class GameData implements ISplitsData {
return gson.toJson(this.segments);
}
public void setSegment(int index, short ticks) {
segments[index] = ticks;
public void setSegment(int index, int ticks) {
segments[index] = (short) ticks;
}
}

View file

@ -3,7 +3,6 @@ package com.github.stachelbeere1248.zombiesutils.timer.recorder.files;
import com.github.stachelbeere1248.zombiesutils.ZombiesUtils;
import com.github.stachelbeere1248.zombiesutils.game.GameMode;
import com.github.stachelbeere1248.zombiesutils.timer.recorder.FileManager;
import com.github.stachelbeere1248.zombiesutils.timer.recorder.SplitsFile;
import com.github.stachelbeere1248.zombiesutils.timer.recorder.data.CategoryData;
import net.minecraft.client.Minecraft;
import net.minecraft.util.ChatComponentText;
@ -12,7 +11,7 @@ import org.jetbrains.annotations.NotNull;
import java.io.File;
public class CategoryFile extends SplitsFile {
public class CategoryFile extends File {
private final CategoryData data;
private final GameMode gameMode;
@ -28,7 +27,7 @@ public class CategoryFile extends SplitsFile {
return this.data.getBestSegment(round - 1);
}
public void setBestSegment(int round, short ticks) {
public void setBestSegment(int round, int ticks) {
this.data.setBestSegment(round - 1, ticks);
try {

View file

@ -4,7 +4,6 @@ package com.github.stachelbeere1248.zombiesutils.timer.recorder.files;
import com.github.stachelbeere1248.zombiesutils.ZombiesUtils;
import com.github.stachelbeere1248.zombiesutils.game.enums.Map;
import com.github.stachelbeere1248.zombiesutils.timer.recorder.FileManager;
import com.github.stachelbeere1248.zombiesutils.timer.recorder.SplitsFile;
import com.github.stachelbeere1248.zombiesutils.timer.recorder.data.GameData;
import net.minecraft.client.Minecraft;
import net.minecraft.util.ChatComponentText;
@ -16,7 +15,7 @@ import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
public class GameFile extends SplitsFile {
public class GameFile extends File {
private final GameData data;
public GameFile(String serverNumber, Map map) {
@ -31,7 +30,7 @@ public class GameFile extends SplitsFile {
return dateTime.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME).replace(':', '-').replaceFirst("T", "_");
}
public void setSegment(int round, short ticks) {
public void setSegment(int round, int ticks) {
this.data.setSegment(round - 1, ticks);
try {

View file

@ -0,0 +1,4 @@
package com.github.stachelbeere1248.zombiesutils.utils;
public class InvalidMapException extends Exception {
}

View file

@ -0,0 +1,6 @@
package com.github.stachelbeere1248.zombiesutils.utils;
public class ScoardboardException extends Exception {
public ScoardboardException() {
}
}

View file

@ -57,7 +57,7 @@ public class Scoreboard {
}
}
public static byte getRound() {
public static int getRound() {
String line;
try {
line = lines.get(2);
@ -91,39 +91,6 @@ public class Scoreboard {
return Optional.ofNullable(string);
}
/* Outdated
public static Optional<Map> getMap() {
String line;
try {
line = lines.get(12); //This was changed!
} catch (Exception couldBePregame) {
try {
line = lines.get(2);
} catch (IndexOutOfBoundsException | NullPointerException ignored) {
return Optional.empty();
}
}
final Pattern MAP_PATTERN = LanguageSupport.mapPattern(ZombiesUtils.getInstance().getConfig().getLanguage());
String mapString = MAP_PATTERN.matcher(line).replaceAll("$1");
switch (mapString) {
case "Dead End":
return Optional.of(Map.DEAD_END);
case "Bad Blood":
return Optional.of(Map.BAD_BLOOD);
case "Alien Arcadium":
return Optional.of(Map.ALIEN_ARCADIUM);
case "Prison":
return Optional.of(Map.PRISON);
default:
return Optional.empty();
}
}
*/
public static int getLineCount() {
return lines.size();
}
public static boolean isNotZombies() {
return (!"ZOMBIES".equals(title));
}