did sum stuff

This commit is contained in:
Moritz Roßbacher 2023-11-01 12:21:27 +01:00
parent d7289148e0
commit b6ab51ddcd
24 changed files with 830 additions and 95 deletions

24
LICENSE
View file

@ -1,24 +0,0 @@
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org/>

View file

@ -1,34 +0,0 @@
# Architectury Loom based template for 1.8.9 forge mods
**For other templates, do check out the [other branches of this repository](https://github.com/romangraef/Forge1.8.9Template/branches/all)**
To get started, clone this repository.
In `build.gradle.kts`, replace the values of `baseGroup` and `group` with your own names.
In `settings.gradle.kts` change `rootProject.name` to your desired mod id.
The `com.example` package needs to be renamed to match the value of `baseGroup`.
If you don't want mixins (which allow for modifying vanilla code), then you can remove the references to mixins from
the `build.gradle.kts` at the lines specified with comments and the `com.example.mixin` package.
This project uses [DevAuth](https://github.com/DJtheRedstoner/DevAuth) per default, so you can log in using your real
minecraft account. If you don't need that, you can remove it from the buildscript.
To run the mod you will need two JDKs, one Java 17 jdk and one Java 1.8 jdk. You can download those
from [here](https://adoptium.net/temurin/releases) (or use your own downloads).
When you import your project into IntelliJ, you need to set the gradle jvm to the Java 17 JDK in the gradle tab, and the
Project SDK to the Java 1.8 JDK. Then click on the sync button in IntelliJ, and it should create a run task
called `Minecraft Client`. If it doesn't then try relaunching your IntelliJ. **Warning for Mac users**: You might have to remove the `-XStartOnFirstThread` vm argument from your run configuration. In the future, that should be handled by the plugin, but for now you'll probably have to do that manually.
To export your project, run the `gradle build` task, and give other people the
file `build/libs/<modid>-<version>.jar`. Ignore the jars in the `build/badjars` folder. Those are intermediary jars that
are used by the build system but *do not work* in a normal forge installation.
### For those who have not an attention span
[![Youtube Tutorial](https://i.ytimg.com/vi/nWzHlomdCgc/maxresdefault.jpg)](https://www.youtube.com/watch?v=nWzHlomdCgc)
## Licensing
This template is licensed under the Unlicense (license copy present in this repository), or alternatively under [Creative Commons 1.0 Universal (CC0 1.0)](https://creativecommons.org/publicdomain/zero/1.0/), and all contributions and PR to this template are expected to follow this. This means your mod, based on this template can be licensed whatever way you want, and does not need to reference back to this template in any way.

View file

@ -8,7 +8,7 @@ plugins {
//Constants:
val baseGroup: String by project
val baseGroup = "com.github.stachelbeere1248.zombiesutils"
val mcVersion: String by project
val version: String by project
val mixinGroup = "$baseGroup.mixin"

View file

@ -1,13 +0,0 @@
package com.github.stachelbeere1248.zombiesutils;
import net.minecraft.init.Blocks;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.event.FMLInitializationEvent;
@Mod(modid = "zombiesutils", useMetadata=true)
public class ExampleMod {
@Mod.EventHandler
public void init(FMLInitializationEvent event) {
System.out.println("Dirt: " + Blocks.dirt.getUnlocalizedName());
}
}

View file

@ -0,0 +1,45 @@
package com.github.stachelbeere1248.zombiesutils;
import com.github.stachelbeere1248.zombiesutils.commands.CategoryCommand;
import com.github.stachelbeere1248.zombiesutils.handlers.TickHandler;
import com.github.stachelbeere1248.zombiesutils.render.TimeRenderer;
import net.minecraftforge.client.ClientCommandHandler;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.common.config.Configuration;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.event.FMLInitializationEvent;
import net.minecraftforge.fml.common.event.FMLPreInitializationEvent;
import org.apache.logging.log4j.Logger;
@Mod(modid = "zombiesutils", useMetadata=true, clientSideOnly = true)
public class ZombiesUtils {
private static ZombiesUtils instance;
private Configuration config;
private Logger logger;
public ZombiesUtils() {
instance = this;
System.out.println("Initialised zombies-utils");
}
public static ZombiesUtils getInstance() {
return instance;
}
@Mod.EventHandler
public void preInit(FMLPreInitializationEvent event) {
logger = event.getModLog();
config = new Configuration(event.getSuggestedConfigurationFile());
config.load();
}
@Mod.EventHandler
public void init(FMLInitializationEvent event) {
MinecraftForge.EVENT_BUS.register(new TimeRenderer());
MinecraftForge.EVENT_BUS.register(new TickHandler());
ClientCommandHandler.instance.registerCommand(new CategoryCommand());
}
public Configuration getConfig() {
return config;
}
public Logger getLogger() {
return logger;
}
}

View file

@ -0,0 +1,48 @@
package com.github.stachelbeere1248.zombiesutils.commands;
import com.github.stachelbeere1248.zombiesutils.timer.Timer;
import com.github.stachelbeere1248.zombiesutils.timer.recorder.Category;
import net.minecraft.command.CommandBase;
import net.minecraft.command.CommandException;
import net.minecraft.command.ICommandSender;
import net.minecraft.util.BlockPos;
import net.minecraft.util.ChatComponentText;
import net.minecraft.util.IChatComponent;
import java.util.Arrays;
import java.util.List;
public class CategoryCommand extends CommandBase {
public CategoryCommand () {
}
@Override
public String getCommandName() {
return "runCategory";
}
@Override
public String getCommandUsage(ICommandSender sender) {
return "runCategory <category-name>";
}
@Override
public void processCommand(ICommandSender sender, String[] args) throws CommandException {
if (args.length == 0) {
IChatComponent error = new ChatComponentText("Please input the name for the category");
sender.addChatMessage(error);
} else {
Category.setSelectedCategory(args[0]);
Timer.getInstance().ifPresent(timer -> timer.setCategory(new Category()));
}
}
@Override
public List<String> addTabCompletionOptions(ICommandSender sender, String[] args, BlockPos blockPos) {
return Arrays.asList(Category.getCategories());
}
@Override
public boolean canCommandSenderUseCommand(ICommandSender sender) {
return true;
}
}

View file

@ -0,0 +1,5 @@
package com.github.stachelbeere1248.zombiesutils.game;
public enum Difficulty {
NORMAL, HARD, RIP
}

View file

@ -0,0 +1,63 @@
package com.github.stachelbeere1248.zombiesutils.game;
import org.jetbrains.annotations.NotNull;
public class GameMode {
private static GameMode currentGameMode = null;
public static final GameMode DEAD_END_NORMAL = new GameMode(Map.DEAD_END), DEAD_END_HARD = new GameMode(Map.DEAD_END, Difficulty.HARD), DEAD_END_RIP = new GameMode(Map.DEAD_END, Difficulty.RIP);
public static final GameMode BAD_BLOOD_NORMAL = new GameMode(Map.BAD_BLOOD), BAD_BLOOD_HARD = new GameMode(Map.BAD_BLOOD, Difficulty.HARD), BAD_BLOOD_RIP = new GameMode(Map.BAD_BLOOD, Difficulty.RIP);
public static final GameMode ALIEN_ARCADIUM = new GameMode(Map.ALIEN_ARCADIUM);
private final Map map;
private final Difficulty difficulty;
private GameMode (@NotNull Map map, @NotNull Difficulty difficulty) {
this.map = map;
this.difficulty = difficulty;
}
private GameMode(@NotNull Map map) {
this.map = map;
this.difficulty = Difficulty.NORMAL;
}
public static void create(@NotNull Map map) {
switch (map) {
case DEAD_END: currentGameMode = DEAD_END_NORMAL; break;
case BAD_BLOOD: currentGameMode = BAD_BLOOD_NORMAL; break;
case ALIEN_ARCADIUM: currentGameMode = ALIEN_ARCADIUM; break;
}
}
public Map getMap() {
return map;
}
public Difficulty getDifficulty() {
return difficulty;
}
public void changeDifficulty(@NotNull Difficulty difficulty) {
switch (map) {
case ALIEN_ARCADIUM:
throw new RuntimeException("Achievement Get: Alien Arcadium Hard/RIP" + Map.ALIEN_ARCADIUM);
case DEAD_END:
switch (difficulty) {
case NORMAL: currentGameMode = DEAD_END_NORMAL; break;
case HARD: currentGameMode = DEAD_END_HARD; break;
case RIP: currentGameMode = DEAD_END_RIP; break;
} break;
case BAD_BLOOD:
switch (difficulty) {
case NORMAL: currentGameMode = BAD_BLOOD_NORMAL; break;
case HARD: currentGameMode = BAD_BLOOD_HARD; break;
case RIP: currentGameMode = BAD_BLOOD_RIP; break;
} break;
}
}
public static GameMode getCurrentGameMode() {
return currentGameMode;
}
/**
* Call to invalidate {@link #currentGameMode} to trigger the garbage collector
*/
public static void drop() {
currentGameMode = null;
}
}

View file

@ -0,0 +1,4 @@
package com.github.stachelbeere1248.zombiesutils.game;
public enum Map {
DEAD_END, BAD_BLOOD, ALIEN_ARCADIUM
}

View file

@ -0,0 +1,56 @@
package com.github.stachelbeere1248.zombiesutils.game;
import org.jetbrains.annotations.NotNull;
public class Waves {
private static final byte[][] deadEndWaveTimes = {{10,20},{10,20},{10,20,35},{10,20,35},{10,22,37},{10,22,44},{10,25,47},{10,25,50},{10,22,38},{10,24,45},{10,25,48},{10,25,50},{10,25,50},{10,25,45},{10,25,46},{10,24,47},{10,24,47},{10,24,47},{10,24,47},{10,24,49},{10,23,44},{10,23,45},{10,23,42},{10,23,43},{10,23,43},{10,23,36},{10,24,44},{10,24,42},{10,24,42},{10,24,45}},
badBloodWaveTimes = {{10,22},{10,22},{10,22},{10,22,34},{10,22,34},{10,22,34},{10,22,34},{10,22,34},{10,22,34},{10,22,34},{10,22,34},{10,22,34},{10,22,34},{10,22,34},{10,22,34},{10,22,34},{10,22,34},{10,22,34},{10,22,34},{10,22,34},{10,22,34},{10,22,34},{10,22,34},{10,22,34},{10,22,34},{10,24,38},{10,24,38},{10,22,34},{10,24,38},{10,22,34}},
alienArcadiumWaveTimes = {{10,13,16,19},{10,14,18,22},{10,13,16,19},{10,14,17,21,25,28},{10,14,18,22,26,30},{10,14,19,23,28,32},{10,15,19,23,27,31},{10,15,20,25,30,35},{10,14,19,23,28,32},{10,16,22,27,33,38},{10,16,21,27,32,38},{10,16,22,28,34,40},{10,16,22,28,34,40},{10,16,21,26,31,36},{10,17,24,31,38,46},{10,16,22,27,33,38},{10,14,19,23,28,32},{10,14,19,23,28,32},{10,14,18,22,26,30},{10,15,21,26,31,36},{10,14,19,23,28,32},{10,14,19,23,28,34},{10,14,18,22,26,30},{10,14,19,23,28,32},{10},{10,23,36},{10,22,34},{10,20,30},{10,24,38},{10,22,34},{10,22,34},{10,21,32},{10,22,34},{10,22,34},{10},{10,22,34},{10,20,31},{10,22,34},{10,22,34},{10,22,34,37,45},{10,21,32},{10,22,34},{10,13,22,25,34,37},{10,22,34},{10,22,34,35},{10,21,32,35},{10,20,30},{10,20,30,33},{10,21,32},{10,22,34,37},{10,20,30,33},{10,22,34,37},{10,22,34,37},{10,20,32,35,39},{10,16,22,28,34,40},{10,14,18},{10,14,18},{10,22,34,37,38},{10,14,18,22,26,30},{10,20,30,33},{10,14,18,22,26,30},{10,14,18,22,26,30},{10,14,18,22,26,30},{10,14,18,22,26,30},{10,14,18,22,26,30},{10,14,18,22,26,30},{10,14,18,22,26,30},{10,14,18,22,26,30},{10,14,18,22,26,30},{10,14,18,22,26,30},{10,14,18,22,26,30},{10,14,18,22,26,30},{10,14,18,22,26,30},{10,14,18,22,26,30},{10,14,18,22,27,32},{10,14,18,22,27,32},{10,14,18,22,26,30},{10,14,18,22,26,30},{10,14,18,22,26,30},{10,14,18,22,26,30},{10,14,18,22,26,30},{10,14,18,22,26,30},{10,14,18,22,26,30},{10,14,18,22,26,30},{10,14,18,22,26,30},{10,14,18,22,26,30},{10,14,18,22,26,30},{10,14,18,22,26,30},{10,14,18,22,26,30},{10,14,18,22,26,30},{10,14,18,22,26,30},{10,14,18,22,26,30},{10,14,18,22,26,30},{10,14,18,22,26,30},{10,14,18,22,26,30},{10,14,18,22,26,30},{10,14,18,22,26,30},{10,14,18,22,26,30},{10,14,18,22,26,30},{10,14,18,22,26,30},{5},{5},{5},{5},{5}}
;
private static final short[] deadEndRoundTimesSum = {0,20,40,75,110,147,191,238,288,326,371,419,469,519,564,610,657,704,751,798,847,891,936,978,1021,1064,1100,1144,1186,1228,1273},
badBloodRoundTimesSum = {0,22,44,66,100,134,168,202,236,270,304,338,372,406,440,474,508,542,576,610,644,678,712,746,780,814,852,890,924,962,996},
alienArcadiumRoundTimesSum = {0,19,41,60,88,118,150,181,216,248,286,324,364,404,440,486,524,556,588,618,654,686,720,750,782,792,828,862,892,930,964,998,1030,1064,1098,1108,1142,1173,1207,1241,1286,1318,1352,1389,1423,1458,1493,1523,1556,1588,1625,1658,1695,1732,1771,1811,1829,1847,1885,1915,1948,1978,2008,2038,2068,2098,2128,2158,2188,2218,2248,2278,2308,2338,2368,2400,2432,2462,2492,2522,2552,2582,2612,2642,2672,2702,2732,2762,2792,2822,2852,2882,2912,2942,2972,3002,3032,3062,3092,3122,3152,3157,3162,3167,3172,3177}
;
public static byte[] get(@NotNull Map map, byte round) {
byte[] waves;
switch (map) {
case DEAD_END:
waves = deadEndWaveTimes[round - 1];
break;
case BAD_BLOOD:
waves = badBloodWaveTimes[round - 1];
break;
case ALIEN_ARCADIUM:
waves = alienArcadiumWaveTimes[round - 1];
break;
default:
throw new IllegalStateException("Unexpected value: " + map);
}
return waves;
}
public static short getSum(@NotNull Map map, byte round) {
short sum;
switch (map) {
case DEAD_END:
sum = deadEndRoundTimesSum[round - 1];
break;
case BAD_BLOOD:
sum = badBloodRoundTimesSum[round - 1];
break;
case ALIEN_ARCADIUM:
sum = alienArcadiumRoundTimesSum[round - 1];
break;
default:
throw new IllegalStateException("Unexpected value: " + map);
}
return sum;
}
public static byte getLastWave(@NotNull Map map, byte round) {
byte[] aByte = get(map, round);
return aByte[aByte.length-1];
}
}

View file

@ -0,0 +1,31 @@
package com.github.stachelbeere1248.zombiesutils.handlers;
import com.github.stachelbeere1248.zombiesutils.ZombiesUtils;
import com.github.stachelbeere1248.zombiesutils.game.Difficulty;
import com.github.stachelbeere1248.zombiesutils.game.GameMode;
import net.minecraftforge.client.event.ClientChatReceivedEvent;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import java.util.regex.Pattern;
public class ChatHandler {
private static final Pattern STRIP_COLOR_PATTERN = Pattern.compile("§[0-9A-FK-ORZ]", Pattern.CASE_INSENSITIVE);
@SubscribeEvent
public void onChatReceived(ClientChatReceivedEvent event) {
String message = STRIP_COLOR_PATTERN.matcher(event.message.getUnformattedText()).replaceAll("").trim();
GameMode gameMode = GameMode.getCurrentGameMode();
if (message.contains(":")) return;
if (gameMode == null) return;
ZombiesUtils.getInstance().getLogger().debug("Chat-event: " + message);
if (message.contains("Hard Difficulty") || message.contains("困难") || message.contains("困難")) {
gameMode.changeDifficulty(Difficulty.HARD);
} else if (message.contains("RIP Difficulty") || message.contains("安息") || message.contains("RIP")) {
gameMode.changeDifficulty(Difficulty.RIP);
}
}
}

View file

@ -0,0 +1,12 @@
package com.github.stachelbeere1248.zombiesutils.handlers;
import com.github.stachelbeere1248.zombiesutils.utils.Scoreboard;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.gameevent.TickEvent;
public class TickHandler {
@SubscribeEvent
public void onTick(TickEvent.ClientTickEvent event) {
Scoreboard.refresh();
}
}

View file

@ -1,16 +0,0 @@
package com.github.stachelbeere1248.zombiesutils.mixin;
import net.minecraft.client.gui.GuiMainMenu;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(GuiMainMenu.class)
public class MixinGuiMainMenu {
@Inject(method = "initGui", at = @At("HEAD"))
public void onInitGui(CallbackInfo ci) {
System.out.println("Hello from Main Menu!");
}
}

View file

@ -0,0 +1,56 @@
package com.github.stachelbeere1248.zombiesutils.mixin;
import com.github.stachelbeere1248.zombiesutils.ZombiesUtils;
import com.github.stachelbeere1248.zombiesutils.timer.Timer;
import com.github.stachelbeere1248.zombiesutils.utils.Scoreboard;
import net.minecraft.client.network.NetHandlerPlayClient;
import net.minecraft.network.play.server.S29PacketSoundEffect;
import org.jetbrains.annotations.NotNull;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(NetHandlerPlayClient.class)
public class MixinNetHandlerPlayClient {
public MixinNetHandlerPlayClient() {}
@Unique
private boolean zombies_utils$alienUfoOpened;
@Inject(method = "handleSoundEffect", at = @At(value = "HEAD"))
private void handleSound(S29PacketSoundEffect packetIn, CallbackInfo ci) {
zombies_utils$handleSound(packetIn);
}
@Unique
private void zombies_utils$handleSound(@NotNull S29PacketSoundEffect packet) {
String soundEffect = packet.getSoundName();
if (!(
soundEffect.equals("mob.wither.spawn")
|| soundEffect.equals("mob.enderdragon.end")
|| (soundEffect.equals("mob.guardian.curse") && !zombies_utils$alienUfoOpened)
)) return;
zombies_utils$alienUfoOpened = soundEffect.equals("mob.guardian.curse");
if (!Timer.getInstance().isPresent()) {
ZombiesUtils.getInstance().getLogger().info("Attempting creation of new timer");
new Timer(
Scoreboard.getServerNumber().orElseThrow(() -> new RuntimeException("cannot figure out servernumber")),
Scoreboard.getMap().orElseThrow(() -> new RuntimeException("cannot figure out map"))
);
} else {
Timer timer = Timer.getInstance().get();
//TODO: GAME END
if (timer.equalsServerOrNull(Scoreboard.getServerNumber().orElse(null))) timer.split(Scoreboard.getRound());
else {
ZombiesUtils.getInstance().getLogger().info("Attempting creation of new timer");
//also kills the previous timer using the garbage collector
new Timer(
Scoreboard.getServerNumber().orElseThrow(() -> new RuntimeException("cannot figure out servernumber")),
Scoreboard.getMap().orElseThrow(() -> new RuntimeException("cannot figure out map"))
);
}
}
}
}

View file

@ -0,0 +1,36 @@
package com.github.stachelbeere1248.zombiesutils.render;
import com.github.stachelbeere1248.zombiesutils.timer.Timer;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.FontRenderer;
import net.minecraft.client.gui.ScaledResolution;
import net.minecraftforge.client.event.RenderGameOverlayEvent;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import java.util.Objects;
public class TimeRenderer {
private final FontRenderer fontRenderer;
public TimeRenderer() {
this.fontRenderer = Objects.requireNonNull(Minecraft.getMinecraft().fontRendererObj, "FontRenderer must not be null!");
}
@SubscribeEvent
public void onRenderGameOverlay(RenderGameOverlayEvent.Post event) {
if (event.type != RenderGameOverlayEvent.ElementType.ALL) return;
if (!Timer.getInstance().isPresent()) return;
long timerTicks = Timer.getInstance().get().roundTime();
long minutesPart = (timerTicks*50) / 60000;
long secondsPart = ((timerTicks*50) % 60000) / 1000;
long tenthSecondsPart = ((timerTicks*50) % 1000) / 100;
String time = String.format("%d:%02d.%d", minutesPart, secondsPart, tenthSecondsPart);
int width = fontRenderer.getStringWidth(time);
ScaledResolution scaledResolution = new ScaledResolution(Minecraft.getMinecraft());
int screenWidth = scaledResolution.getScaledWidth();
int screenHeight = scaledResolution.getScaledHeight();
int color = 0xFFFFFF;
fontRenderer.drawStringWithShadow(time, screenWidth - width, screenHeight - fontRenderer.FONT_HEIGHT, color);
}
}

View file

@ -0,0 +1,72 @@
package com.github.stachelbeere1248.zombiesutils.timer;
import com.github.stachelbeere1248.zombiesutils.game.GameMode;
import com.github.stachelbeere1248.zombiesutils.timer.recorder.Category;
import com.github.stachelbeere1248.zombiesutils.timer.recorder.TimesFile;
import net.minecraft.client.Minecraft;
import net.minecraft.util.*;
public class RecordManager {
public static void compareSegment(byte round, short roundTime, Category category) {
TimesFile timesFile = category.getByGameMode(GameMode.getCurrentGameMode());
short bestSegment = timesFile.getBestSegment(round);
if (roundTime<bestSegment) {
timesFile.setBestSegment(round, roundTime);
Minecraft.getMinecraft().thePlayer.addChatMessage(new ChatComponentText(
"\u00a7l\u00a7e" + category.getName() + ": ***" + "\u00a7l\u00a76 NEW BEST SEGMENT! " + "\u00a7l\u00a7e***"
));
final String timeString = getTimeString(roundTime);
final String message = "\u00a7cRound " + round + "\u00a7e took \u00a7a" + timeString + " \u00a79\u03B4" + (double) (roundTime-bestSegment)/20;
Minecraft.getMinecraft().thePlayer.addChatMessage(new ChatComponentText(message));
} else if (bestSegment == (short) 0) {
timesFile.setBestSegment(round, roundTime);
Minecraft.getMinecraft().thePlayer.addChatMessage(new ChatComponentText(
"\u00a7l\u00a7e" + category.getName() + ": ***" + "\u00a7l\u00a76 NEW BEST SEGMENT! " + "\u00a7l\u00a7e***"
));
final String timeString = getTimeString(roundTime);
final String message = "\u00a7cRound " + round + "\u00a7e took \u00a7a" + timeString + "\u00a7e!";
Minecraft.getMinecraft().thePlayer.addChatMessage(new ChatComponentText(message));
}
}
public static void compareBest(byte round, int gameTime, Category category) {
TimesFile timesFile = category.getByGameMode(GameMode.getCurrentGameMode());
int personalBest = timesFile.getPersonalBest(round);
if (gameTime<personalBest) {
timesFile.setPersonalBest(round, gameTime);
Minecraft.getMinecraft().thePlayer.addChatMessage(new ChatComponentText(
"\u00a7l\u00a7e" + category.getName() + ": ***" + "\u00a7l\u00a76 NEW PERSONAL BEST! " + "\u00a7l\u00a7e***"
));
final String timeString = getTimeString(gameTime);
final String message = "\u00a7cRound " + round + "\u00a7e took \u00a7a" + timeString + " \u00a79\u03B4" + (double) (gameTime-personalBest)/20;
Minecraft.getMinecraft().thePlayer.addChatMessage(new ChatComponentText(message));
} else if (personalBest == 0) {
timesFile.setPersonalBest(round, gameTime);
Minecraft.getMinecraft().thePlayer.addChatMessage(new ChatComponentText(
"\u00a7l\u00a7e" + category.getName() + ": ***" + "\u00a7l\u00a76 NEW PERSONAL BEST! " + "\u00a7l\u00a7e***"
));
final String timeString = getTimeString(gameTime);
final String message = "\u00a7cRound " + round + "\u00a7e took \u00a7a" + timeString + "\u00a7e!";
Minecraft.getMinecraft().thePlayer.addChatMessage(new ChatComponentText(message));
}
}
private static String getTimeString(int gameTime) {
return String.format("%d:%02d.%d",
(gameTime *50) / 60000,
((gameTime *50) % 60000) / 1000,
((gameTime *50) % 1000) / 100
);
}
}

View file

@ -0,0 +1,95 @@
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.Map;
//import com.github.stachelbeere1248.zombiesutils.game.Waves;
import com.github.stachelbeere1248.zombiesutils.timer.recorder.Category;
import net.minecraft.client.Minecraft;
import org.jetbrains.annotations.NotNull;
import java.util.Optional;
public class Timer {
private static Timer instance;
private final long savedTotalWorldTime;
private int passedRoundsTickSum = 0;
private final String serverNumber;
public Category category;
private boolean pbTracking = false;
private byte dontDupeSplitPlease = 0;
/**
* Constructs a timer and saves it to {@link #instance}.
* @param serverNumber The game's server the timer should be bound to.
* @param enumMap The map the timer should be started for.
*/
public Timer (@NotNull String serverNumber, @NotNull Map enumMap) {
instance = this;
savedTotalWorldTime = getCurrentTotalWorldTime();
if (!serverNumber.trim().isEmpty()) this.serverNumber = serverNumber.trim();
else throw new RuntimeException("invalid servernumber");
this.category = new Category();
GameMode.create(enumMap);
}
/**
* 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) {
if (dontDupeSplitPlease == passedRound) {
ZombiesUtils.getInstance().getLogger().debug("SPLIT CANCELLED");
return;
}
if (passedRound == (byte) 1) pbTracking = true;
final int gameTime = gameTime();
final short roundTime = (short) (gameTime - passedRoundsTickSum);
//short clearTime = (short) (roundTime - Waves.getLastWave(GameMode.getCurrentGameMode().getMap(), passedRound));
//ZombiesUtils.getInstance().getLogger().debug("ClearTime: " + clearTime);
ZombiesUtils.getInstance().getLogger().debug("Passed round: " + passedRound);
RecordManager.compareSegment(passedRound, roundTime, category);
if (pbTracking) RecordManager.compareBest(passedRound, gameTime, category);
passedRoundsTickSum = gameTime;
dontDupeSplitPlease = passedRound;
}
private long getCurrentTotalWorldTime() {
if (Minecraft.getMinecraft() == null) return 0;
if (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);
}
public boolean equalsServerOrNull(String serverNumber) {
return (serverNumber == null || serverNumber.equals(this.serverNumber) || serverNumber.isEmpty());
}
public void setCategory(Category category) {
this.category = category;
}
public static Optional<Timer> getInstance() {
return Optional.ofNullable(instance);
}
/**
* Call to invalidate {@link #instance} to trigger the garbage collector
*/
public static void drop() {
instance = null;
GameMode.drop();
}
}

View file

@ -0,0 +1,56 @@
package com.github.stachelbeere1248.zombiesutils.timer.recorder;
import com.github.stachelbeere1248.zombiesutils.ZombiesUtils;
import com.github.stachelbeere1248.zombiesutils.game.GameMode;
import com.github.stachelbeere1248.zombiesutils.timer.Timer;
import org.jetbrains.annotations.NotNull;
import scala.reflect.io.Directory;
import java.io.File;
import java.util.List;
public class Category {
private static String selectedCategory = "default"; // read from config ?
public final TimesFile[] timesFiles = new TimesFile[7];
private final String name;
public Category() {
timesFiles[0] = new TimesFile(selectedCategory, GameMode.DEAD_END_NORMAL);
timesFiles[1] = new TimesFile(selectedCategory, GameMode.DEAD_END_HARD);
timesFiles[2] = new TimesFile(selectedCategory, GameMode.DEAD_END_RIP);
timesFiles[3] = new TimesFile(selectedCategory, GameMode.BAD_BLOOD_NORMAL);
timesFiles[4] = new TimesFile(selectedCategory, GameMode.BAD_BLOOD_HARD);
timesFiles[5] = new TimesFile(selectedCategory, GameMode.BAD_BLOOD_RIP);
timesFiles[6] = new TimesFile(selectedCategory, GameMode.ALIEN_ARCADIUM);
name = selectedCategory;
}
public TimesFile getByGameMode(@NotNull GameMode gameMode) {
if (gameMode.equals(GameMode.DEAD_END_NORMAL)) return timesFiles[0];
if (gameMode.equals(GameMode.DEAD_END_HARD)) return timesFiles[1];
if (gameMode.equals(GameMode.DEAD_END_RIP)) return timesFiles[2];
if (gameMode.equals(GameMode.BAD_BLOOD_NORMAL)) return timesFiles[3];
if (gameMode.equals(GameMode.BAD_BLOOD_HARD)) return timesFiles[4];
if (gameMode.equals(GameMode.BAD_BLOOD_RIP)) return timesFiles[5];
if (gameMode.equals(GameMode.ALIEN_ARCADIUM)) return timesFiles[6];
throw new IllegalStateException("Unexpected value: " + gameMode);
}
public static void setSelectedCategory(String selectedCategory) {
Category.selectedCategory = selectedCategory;
if (!Timer.getInstance().isPresent()) return;
Timer.getInstance().get().category = new Category();
}
public static String[] getCategories() {
File dir = new File("zombies");
if (dir.isDirectory()) return dir.list();
else return new String[0];
}
public String getName() {
return name;
}
}

View file

@ -0,0 +1,39 @@
package com.github.stachelbeere1248.zombiesutils.timer.recorder;
import com.github.stachelbeere1248.zombiesutils.game.Map;
import com.google.gson.Gson;
import java.util.Arrays;
public class FileData{
private final short[] bestSegments; //in ticks, max ~27 min
private final int[] personalBests; //in ticks,
public FileData(Map map) {
if (map == Map.ALIEN_ARCADIUM) {
bestSegments = new short[105];
personalBests = new int[105];
} else {
bestSegments = new short[30];
personalBests = new int[30];
}
Arrays.fill(bestSegments, (short) 0);
Arrays.fill(personalBests, 0);
}
String getAsJsonString() {
Gson gson = new Gson();
return gson.toJson(this, FileData.class);
}
public short getBestSegment(int index) {
return bestSegments[index];
}
public int getPersonalBest(int index) {
return personalBests[index];
}
void setBestSegment(int index, short ticks) {
bestSegments[index] = ticks;
}
void setPersonalBest(int index, int ticks) {
personalBests[index] = ticks;
}
}

View file

@ -0,0 +1,54 @@
package com.github.stachelbeere1248.zombiesutils.timer.recorder;
import com.google.gson.Gson;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
public class FileManager {
private static FileData readDataFromFile(File file) throws FileNotFoundException {
if (!file.exists()) throw new FileNotFoundException();
String dataJson;
Gson gson = new Gson();
try {
dataJson = FileUtils.readFileToString(file, StandardCharsets.UTF_16);
} catch (IOException e) {
throw new RuntimeException(e);
}
return gson.fromJson(dataJson, FileData.class);
}
private static void createDataFile(FileData fileData, File file) {
try {
//noinspection ResultOfMethodCallIgnored
file.getParentFile().mkdirs();
//noinspection ResultOfMethodCallIgnored
file.createNewFile();
} catch (IOException e) {
throw new RuntimeException(e);
}
writeDataToFile(fileData, file);
}
public static void writeDataToFile(FileData fileData, File file) {
try {
FileUtils.writeStringToFile(file, fileData.getAsJsonString(), StandardCharsets.UTF_16);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static FileData readOrCreate(TimesFile file) {
FileData data;
try {
data = FileManager.readDataFromFile(file);
} catch (FileNotFoundException ignored) {
data = new FileData(file.getGameMode().getMap());
FileManager.createDataFile(data, file);
}
return data;
}
}

View file

@ -0,0 +1,38 @@
package com.github.stachelbeere1248.zombiesutils.timer.recorder;
import com.github.stachelbeere1248.zombiesutils.game.GameMode;
import org.jetbrains.annotations.NotNull;
import scala.reflect.io.Directory;
import java.io.File;
public class TimesFile extends File {
private final FileData fileData;
private final GameMode gameMode;
public TimesFile(String category, GameMode gameMode) {
// Game-directory -> custom category -> file named "MAP_DIFFICULTY.times"
// Content encoded in StandardCharsets.UTF_16
super("zombies" + File.separator + category,gameMode.getMap() + "_" + gameMode.getDifficulty() + ".times");
this.gameMode = gameMode;
fileData = FileManager.readOrCreate(this);
}
public short getBestSegment(int round) {
return fileData.getBestSegment(round-1);
}
public void setBestSegment(int round, short ticks) {
fileData.setBestSegment(round-1, ticks);
FileManager.writeDataToFile(fileData,this);
}
public int getPersonalBest(int round) {
return fileData.getPersonalBest(round-1);
}
public void setPersonalBest(int round, int ticks) {
fileData.setPersonalBest(round-1, ticks);
FileManager.writeDataToFile(fileData,this);
}
GameMode getGameMode() {
return gameMode;
}
}

View file

@ -0,0 +1,112 @@
package com.github.stachelbeere1248.zombiesutils.utils;
import com.github.stachelbeere1248.zombiesutils.ZombiesUtils;
import com.github.stachelbeere1248.zombiesutils.game.Map;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import net.minecraft.client.Minecraft;
import net.minecraft.scoreboard.Score;
import net.minecraft.scoreboard.ScoreObjective;
import net.minecraft.scoreboard.ScorePlayerTeam;
import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
public class Scoreboard {
private static final Pattern SIDEBAR_EMOJI_PATTERN = Pattern.compile("[\uD83D\uDD2B\uD83C\uDF6B\uD83D\uDCA3\uD83D\uDC7D\uD83D\uDD2E\uD83D\uDC0D\uD83D\uDC7E\uD83C\uDF20\uD83C\uDF6D\u26BD\uD83C\uDFC0\uD83D\uDC79\uD83C\uDF81\uD83C\uDF89\uD83C\uDF82]+");
private static final Pattern STRIP_COLOR_PATTERN = Pattern.compile("§[0-9A-FK-ORZ]", Pattern.CASE_INSENSITIVE);
private static final Pattern ROUND_LINE_PATTERN = Pattern.compile("(Round )([0-9]{1,3})"); //TODO: Chinese pattern ??
private static final Pattern SERVER_NUMBER_PATTERN = Pattern.compile(".*([mLM][0-9A-Z]+)");
private static final Pattern MAP_PATTERN = Pattern.compile("Map:.*(Dead End|Bad Blood|Alien Arcadium)");
private static String title = null;
private static List<String> lines = null;
/**
* Overrides {@link #title} and {@link #lines} if a valid scoreboard is present
*/
public static void refresh() {
// Null-checks
if ((Minecraft.getMinecraft() == null)
|| (Minecraft.getMinecraft().theWorld == null)
|| Minecraft.getMinecraft().isSingleplayer()
) return;
if (Minecraft.getMinecraft().thePlayer == null) return;
net.minecraft.scoreboard.Scoreboard scoreboard = Minecraft.getMinecraft().theWorld.getScoreboard();
ScoreObjective sidebar = scoreboard.getObjectiveInDisplaySlot(1);
if (sidebar == null) return;
// get
title = sidebar.getDisplayName().trim();
Collection<Score> scoreCollection = scoreboard.getSortedScores(sidebar);
List<Score> filteredScores = scoreCollection.stream().filter(input -> input.getPlayerName() != null && !input.getPlayerName().startsWith("#")).collect(Collectors.toList());
List<Score> scores;
if (filteredScores.size() > 15) scores = Lists.newArrayList(Iterables.skip(filteredScores, scoreCollection.size() - 15));
else scores = filteredScores;
scores = Lists.reverse(scores);
lines = new ArrayList<String>();
for (Score score: scores
) {
ScorePlayerTeam team = scoreboard.getPlayersTeam(score.getPlayerName());
String scoreboardLine = ScorePlayerTeam.formatPlayerName(team, score.getPlayerName()).trim();
lines.add(STRIP_COLOR_PATTERN.matcher(SIDEBAR_EMOJI_PATTERN.matcher(scoreboardLine).replaceAll("")).replaceAll(""));
}
}
public static byte getRound() {
String line;
try {
line = lines.get(2);
} catch (IndexOutOfBoundsException | NullPointerException ignored) {
return 0;
}
String string = ROUND_LINE_PATTERN.matcher(line).replaceAll("$2");
byte round;
try {
round = Byte.parseByte(string);
ZombiesUtils.getInstance().getLogger().debug("Round: " + round);
return round;
} catch (NumberFormatException ignored) {
return 0;
}
}
public static Optional<String> getServerNumber() {
String line;
try {
line = lines.get(0);
} catch (IndexOutOfBoundsException | NullPointerException ignored) {
return Optional.empty();
}
String string = SERVER_NUMBER_PATTERN.matcher(line).replaceAll("$1");
ZombiesUtils.getInstance().getLogger().debug("Servernumber: " + string);
return Optional.ofNullable(string);
}
public static Optional<Map> getMap() {
String line;
try {
line = lines.get(12);
} catch (Exception couldBePregame) {
try {
line = lines.get(2);
} catch (IndexOutOfBoundsException | NullPointerException ignored) {
return Optional.empty();
}
}
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);
default: return Optional.empty();
}
}
public static String getTitle() {
return title;
}
}

View file

@ -1,16 +1,16 @@
[
{
"modid": "${modid}",
"name": "Xample Mod",
"description": "A mod that is used as an example.",
"name": "Zombies Utils",
"description": "",
"version": "${version}",
"mcversion": "${mcversion}",
"url": "https://github.com/romangraef/Forge1.8.9Template/",
"url": "https://github.com/Stachelbeere1248/zombies-utils",
"updateUrl": "",
"authorList": [
"You"
"Stachelbeere1248"
],
"credits": "",
"credits": "Seosean, thamid-23",
"logoFile": "",
"screenshots": [],
"dependencies": []

View file

@ -1,11 +1,11 @@
{
"package": "${mixinGroup}",
"package": "com.github.stachelbeere1248.zombiesutils.mixin",
"refmap": "mixins.${modid}.refmap.json",
"minVersion": "0.7",
"compatibilityLevel": "JAVA_8",
"mixins": [
],
"client": [
"MixinGuiMainMenu"
"MixinNetHandlerPlayClient"
]
}