oi
This commit is contained in:
parent
7a8834d003
commit
8a64b024ef
@ -6,7 +6,7 @@ minecraft_version=1.21.4
|
||||
yarn_mappings=1.21.4+build.8
|
||||
loader_version=0.16.10
|
||||
# Mod Properties
|
||||
mod_version=1.14.514.004
|
||||
mod_version=1.14.514.005
|
||||
maven_group=org.branulf
|
||||
archives_base_name=branulf_toolbox
|
||||
# Dependencies
|
||||
|
@ -2,6 +2,7 @@ package org.branulf.toolbox;
|
||||
|
||||
import org.branulf.toolbox.autocrossbow.AutoCrossbowHandler;
|
||||
import org.branulf.toolbox.crazylook.CrazyLookHandler;
|
||||
import org.branulf.toolbox.damagelogger.DamageLoggerHandler;
|
||||
import org.branulf.toolbox.elytraboost.ElytraBoostCommon;
|
||||
import org.branulf.toolbox.elytraboost.ElytraBoostHandler;
|
||||
import org.branulf.toolbox.playerdetector.PlayerDetectorHandler;
|
||||
@ -16,24 +17,28 @@ import net.minecraft.client.option.KeyBinding;
|
||||
import net.minecraft.client.util.InputUtil;
|
||||
import net.minecraft.text.Text;
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class Main implements ClientModInitializer {
|
||||
public static final String MOD_ID = "branulf_toolbox";
|
||||
private static Main instance;
|
||||
private static ModConfig config;
|
||||
public static final Logger LOGGER = LoggerFactory.getLogger(MOD_ID);
|
||||
|
||||
private static KeyBinding openMainGuiKeyBinding;
|
||||
|
||||
/** 该mod为多个mod组成的拼好mod
|
||||
* 每个mod的处理程序均在单独的软件包(文件夹)中以便于维护
|
||||
* 每个mod的处理程序均在单独的软件包(文件夹)中以便于维护。
|
||||
* <p>
|
||||
* 为什么会存在这个mod?
|
||||
* 因为作者制作了多个小mod,但是为了防止以后数量多导致的维护困难,作者决定将这些mod进行整合,并创建此mod
|
||||
* 这不是个什么正经项目,只是作者想把一些小mod整合起来,方便管理
|
||||
* 因为作者制作了多个小mod,但是为了防止以后数量多导致的维护困难,作者决定将这些mod进行整合,并创建此mod。
|
||||
* 这不是个什么正经项目,只是作者想把一些小mod整合起来,方便管理。
|
||||
* <p>
|
||||
* 我不确定以后我会不会做扩展包,就像carpet那样
|
||||
* 老实说,我能改掉一些开发中的坏习惯已经很累了,在这之前很多时候变量名我都是瞎写的,我有几个py的脚本写的全是屎山,从写jvav开始才改了我的部分坏习惯
|
||||
* 同时,人工智能真好用(),哈哈哈。
|
||||
* 我不确定以后我会不会做扩展包,就像carpet那样。
|
||||
* 老实说,我能改掉一些开发中的坏习惯已经很累了,在这之前很多时候变量名我都是瞎写的,我有几个py的脚本写的全是屎山,从写jvav开始才改了我的部分坏习惯。
|
||||
* <p>
|
||||
* 同时,人工智能真好用(之前很多部分都有依赖于AI,现在算是没之前那样了,之前真的啥都丢给AI,我是干啥的?不过话又说回来,没AI我也学不会这个),哈哈哈。
|
||||
*/
|
||||
|
||||
public AutoCrossbowHandler autoCrossbowHandler;
|
||||
@ -41,6 +46,7 @@ public class Main implements ClientModInitializer {
|
||||
public CrazyLookHandler crazyLookHandler;
|
||||
public ElytraBoostHandler elytraBoostHandler;
|
||||
public PlayerDetectorHandler playerDetectorHandler;
|
||||
public DamageLoggerHandler damageLoggerHandler;
|
||||
|
||||
@Override
|
||||
public void onInitializeClient() {
|
||||
@ -61,6 +67,7 @@ public class Main implements ClientModInitializer {
|
||||
crazyLookHandler = new CrazyLookHandler(config);
|
||||
elytraBoostHandler = new ElytraBoostHandler(config);
|
||||
playerDetectorHandler = new PlayerDetectorHandler(config);
|
||||
damageLoggerHandler = new DamageLoggerHandler(config.damageLogger);
|
||||
|
||||
ClientTickEvents.END_CLIENT_TICK.register(client -> {
|
||||
while (openMainGuiKeyBinding.wasPressed()) {
|
||||
@ -74,6 +81,14 @@ public class Main implements ClientModInitializer {
|
||||
elytraBoostHandler.onClientTick(client);
|
||||
scrafitiptyPlusHandler.onClientTick(client);
|
||||
playerDetectorHandler.onClientTick(client);
|
||||
|
||||
if (damageLoggerHandler != null) {
|
||||
if (config.damageLogger.enabled && !damageLoggerHandler.isLogging()) {
|
||||
damageLoggerHandler.startLogging();
|
||||
} else if (!config.damageLogger.enabled && damageLoggerHandler.isLogging()) {
|
||||
damageLoggerHandler.stopLogging();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
new ElytraBoostCommon().onInitialize();
|
||||
|
@ -3,6 +3,7 @@ package org.branulf.toolbox;
|
||||
import net.minecraft.client.gui.widget.TextFieldWidget;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import org.branulf.toolbox.autocrossbow.AutoCrossbowMode;
|
||||
import org.branulf.toolbox.damagelogger.DamageLoggerHandler;
|
||||
import org.branulf.toolbox.scrafitiptyplus.ScrafitiptyPlusHandler;
|
||||
import me.shedaniel.autoconfig.AutoConfig;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
@ -35,6 +36,11 @@ public class MainScreen extends Screen {
|
||||
private ButtonWidget configButton;
|
||||
private TextFieldWidget playerDetectorRangeField;
|
||||
|
||||
/**
|
||||
* 使用无法点击的按钮作为标题,是为了方便将所有UI元素(包括标题)都作为一个子类来管理,
|
||||
* 尤其是在有滚动条的界面中,这样可以统一处理布局和渲染逻辑,绝对不是因为懒。
|
||||
*/
|
||||
|
||||
public MainScreen(Text title) {
|
||||
super(title);
|
||||
}
|
||||
@ -80,6 +86,11 @@ public class MainScreen extends Screen {
|
||||
currentContentY = addPlayerDetectorButtons(currentContentY);
|
||||
currentContentY += SECTION_SPACING;
|
||||
|
||||
// 伤害记录
|
||||
currentContentY = addSectionTitle(Text.translatable("gui.branulf_toolbox.damage_logger.title").formatted(Formatting.RED), currentContentY);
|
||||
currentContentY = addDamageLoggerButtons(currentContentY);
|
||||
currentContentY += SECTION_SPACING;
|
||||
|
||||
totalContentHeight = currentContentY;
|
||||
|
||||
// 配置
|
||||
@ -279,6 +290,50 @@ public class MainScreen extends Screen {
|
||||
return row2Y + BUTTON_HEIGHT + PADDING;
|
||||
}
|
||||
|
||||
// 伤害记录
|
||||
private int addDamageLoggerButtons(int currentContentY) {
|
||||
DamageLoggerHandler handler = Main.getInstance().damageLoggerHandler;
|
||||
ModConfig.DamageLoggerConfig config = Main.getConfig().damageLogger;
|
||||
|
||||
int centerX = this.width / 2;
|
||||
int row1StartX = centerX - (BUTTON_WIDTH * 2 + PADDING) / 2;
|
||||
|
||||
// 启用
|
||||
ButtonWidget damageLoggerOnButton = ButtonWidget.builder(
|
||||
Text.translatable("gui.branulf_toolbox.damage_logger.enable").formatted(Formatting.GREEN),
|
||||
button -> {
|
||||
config.enabled = true;
|
||||
Main.saveConfig();
|
||||
updateButtonStates();
|
||||
})
|
||||
.dimensions(row1StartX, currentContentY, BUTTON_WIDTH, BUTTON_HEIGHT)
|
||||
.build();
|
||||
scrollableWidgets.add(damageLoggerOnButton);
|
||||
|
||||
// 禁用
|
||||
ButtonWidget damageLoggerOffButton = ButtonWidget.builder(
|
||||
Text.translatable("gui.branulf_toolbox.damage_logger.disable").formatted(Formatting.RED),
|
||||
button -> {
|
||||
config.enabled = false;
|
||||
Main.saveConfig();
|
||||
updateButtonStates();
|
||||
})
|
||||
.dimensions(row1StartX + BUTTON_WIDTH + PADDING, currentContentY, BUTTON_WIDTH, BUTTON_HEIGHT)
|
||||
.build();
|
||||
scrollableWidgets.add(damageLoggerOffButton);
|
||||
|
||||
// 打开文件夹
|
||||
int row2Y = currentContentY + BUTTON_HEIGHT + PADDING;
|
||||
ButtonWidget openLogFolderButton = ButtonWidget.builder(
|
||||
Text.translatable("gui.branulf_toolbox.damage_logger.open_folder"),
|
||||
button -> handler.openLogFolder())
|
||||
.dimensions(centerX - BUTTON_WIDTH / 2, row2Y, BUTTON_WIDTH, BUTTON_HEIGHT)
|
||||
.build();
|
||||
scrollableWidgets.add(openLogFolderButton);
|
||||
|
||||
return row2Y + BUTTON_HEIGHT + PADDING;
|
||||
}
|
||||
|
||||
private void updateButtonStates() {
|
||||
ModConfig config = Main.getConfig();
|
||||
ScrafitiptyPlusHandler scrafitiptyPlusHandler = Main.getInstance().scrafitiptyPlusHandler;
|
||||
@ -327,6 +382,12 @@ public class MainScreen extends Screen {
|
||||
.append(Text.literal(" (" + (config.playerDetector.enabled ? Text.translatable("branulf_toolbox.enabled").getString() : Text.translatable("branulf_toolbox.disabled").getString()) + ")"))
|
||||
.formatted(config.playerDetector.enabled ? Formatting.GREEN : Formatting.RED));
|
||||
}
|
||||
// 伤害记录
|
||||
else if (button.getMessage().getString().contains(Text.translatable("gui.branulf_toolbox.damage_logger.enable").getString())) {
|
||||
button.active = !config.damageLogger.enabled;
|
||||
} else if (button.getMessage().getString().contains(Text.translatable("gui.branulf_toolbox.damage_logger.disable").getString())) {
|
||||
button.active = config.damageLogger.enabled;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (playerDetectorRangeField != null) {
|
||||
|
@ -35,8 +35,15 @@ public class ModConfig implements ConfigData {
|
||||
@ConfigEntry.Gui.TransitiveObject
|
||||
public PlayerDetectorConfig playerDetector = new PlayerDetectorConfig();
|
||||
|
||||
// 伤害记录
|
||||
@ConfigEntry.Category("damage_logger_settings")
|
||||
@ConfigEntry.Gui.TransitiveObject
|
||||
public DamageLoggerConfig damageLogger = new DamageLoggerConfig();
|
||||
|
||||
|
||||
// ————————————————————分割线————————————————————
|
||||
|
||||
// 诸葛连弩
|
||||
public static class AutoCrossbowConfig implements ConfigData {
|
||||
@ConfigEntry.Gui.Tooltip(count = 0)
|
||||
public boolean enabled = false;
|
||||
@ -44,8 +51,15 @@ public class ModConfig implements ConfigData {
|
||||
@ConfigEntry.Gui.EnumHandler(option = ConfigEntry.Gui.EnumHandler.EnumDisplayOption.BUTTON)
|
||||
@ConfigEntry.Gui.Tooltip(count = 0)
|
||||
public AutoCrossbowMode currentMode = AutoCrossbowMode.NORMAL;
|
||||
|
||||
@ConfigEntry.Gui.Tooltip(count = 1)
|
||||
public boolean compatibilityMode = false;
|
||||
|
||||
@ConfigEntry.Gui.Tooltip(count = 1)
|
||||
public boolean ultimateCompatibilityMode = false;
|
||||
}
|
||||
|
||||
// Scrafitipty
|
||||
public static class ScrafitiptyPlusConfigPart implements ConfigData {
|
||||
@ConfigEntry.Gui.Tooltip(count = 1)
|
||||
public int port = 8765;
|
||||
@ -68,6 +82,7 @@ public class ModConfig implements ConfigData {
|
||||
}
|
||||
}
|
||||
|
||||
// 挂机
|
||||
public static class CrazyLookConfig implements ConfigData {
|
||||
@ConfigEntry.Gui.Tooltip(count = 0)
|
||||
public boolean enabled = false;
|
||||
@ -76,12 +91,14 @@ public class ModConfig implements ConfigData {
|
||||
public int shakeFrequency = 2;
|
||||
}
|
||||
|
||||
// 鞘翅推进优化
|
||||
public static class ElytraBoostConfigPart implements ConfigData {
|
||||
@ConfigEntry.Gui.Tooltip(count = 1)
|
||||
public boolean modEnabled = true;
|
||||
|
||||
}
|
||||
|
||||
// 玩家探测器
|
||||
public static class PlayerDetectorConfig implements ConfigData {
|
||||
@ConfigEntry.Gui.Tooltip(count = 0)
|
||||
public boolean enabled = false;
|
||||
@ -94,4 +111,13 @@ public class ModConfig implements ConfigData {
|
||||
@ConfigEntry.BoundedDiscrete(min = 1, max = 20)
|
||||
public int alertFrequency = 1;
|
||||
}
|
||||
|
||||
// 伤害记录
|
||||
public static class DamageLoggerConfig implements ConfigData {
|
||||
@ConfigEntry.Gui.Tooltip(count = 0)
|
||||
public boolean enabled = false;
|
||||
|
||||
@ConfigEntry.Gui.Tooltip(count = 1)
|
||||
public boolean rememberState = false;
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager;
|
||||
import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback;
|
||||
import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.component.DataComponentTypes;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.entity.player.PlayerInventory;
|
||||
import net.minecraft.item.CrossbowItem;
|
||||
@ -22,6 +23,7 @@ import net.minecraft.text.Text;
|
||||
import net.minecraft.util.Hand;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class AutoCrossbowHandler {
|
||||
|
||||
@ -33,14 +35,19 @@ public class AutoCrossbowHandler {
|
||||
private long lastActionTime = 0;
|
||||
private static final long ACTION_COOLDOWN_MS = 50;
|
||||
|
||||
private boolean isCrossbowInternallyCharged = false;
|
||||
private ItemStack lastHeldCrossbowStack = ItemStack.EMPTY;
|
||||
|
||||
public AutoCrossbowHandler(ModConfig mainConfig) {
|
||||
this.config = mainConfig.autocrossbow;
|
||||
registerCommands();
|
||||
Main.LOGGER.info("BRanulf的实用工具箱 - 诸葛连弩模块已加载!");
|
||||
}
|
||||
|
||||
public void onClientTick(MinecraftClient client) {
|
||||
if (!config.enabled || client.player == null || client.interactionManager == null) {
|
||||
resetActionStates();
|
||||
lastHeldCrossbowStack = ItemStack.EMPTY;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -52,9 +59,17 @@ public class AutoCrossbowHandler {
|
||||
|
||||
if (!(stack.getItem() instanceof CrossbowItem)) {
|
||||
resetActionStates();
|
||||
lastHeldCrossbowStack = ItemStack.EMPTY;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ItemStack.areItemsAndComponentsEqual(stack, lastHeldCrossbowStack)) {
|
||||
isCrossbowInternallyCharged = false;
|
||||
chargeProgress = 0;
|
||||
lastHeldCrossbowStack = stack.copy();
|
||||
}
|
||||
|
||||
|
||||
switch (config.currentMode) {
|
||||
case NORMAL:
|
||||
handleNormalMode(client, player, hand, stack);
|
||||
@ -74,10 +89,46 @@ public class AutoCrossbowHandler {
|
||||
wasRightClickPressed = false;
|
||||
chargeProgress = 0;
|
||||
lastActionTime = 0;
|
||||
isCrossbowInternallyCharged = false;
|
||||
}
|
||||
|
||||
private boolean isCrossbowEffectivelyCharged(ItemStack stack) {
|
||||
if (config.compatibilityMode) {
|
||||
List<ItemStack> chargedProjectiles = stack.get(DataComponentTypes.CHARGED_PROJECTILES).getProjectiles();
|
||||
return chargedProjectiles != null && !chargedProjectiles.isEmpty();
|
||||
} else {
|
||||
return CrossbowItem.isCharged(stack);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleNormalMode(MinecraftClient client, PlayerEntity player, Hand hand, ItemStack stack) {
|
||||
if (CrossbowItem.isCharged(stack) && isRightClickPressed) {
|
||||
if (config.ultimateCompatibilityMode) {
|
||||
if (isRightClickPressed) {
|
||||
if (isCrossbowInternallyCharged) {
|
||||
client.interactionManager.interactItem(player, hand);
|
||||
isCrossbowInternallyCharged = false;
|
||||
chargeProgress = 0;
|
||||
} else {
|
||||
if (!player.isUsingItem()) {
|
||||
client.interactionManager.interactItem(player, hand);
|
||||
chargeProgress = 0;
|
||||
} else if (player.getActiveHand() == hand) {
|
||||
chargeProgress++;
|
||||
int pullTime = CrossbowItem.getPullTime(stack, player);
|
||||
if (chargeProgress >= pullTime * 1.1f) {
|
||||
client.interactionManager.stopUsingItem(player);
|
||||
isCrossbowInternallyCharged = true;
|
||||
chargeProgress = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (wasRightClickPressed) {
|
||||
chargeProgress = 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (isCrossbowEffectivelyCharged(stack) && isRightClickPressed) {
|
||||
client.interactionManager.interactItem(player, hand);
|
||||
return;
|
||||
}
|
||||
@ -97,6 +148,7 @@ public class AutoCrossbowHandler {
|
||||
chargeProgress = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleRapidFireShootMode(MinecraftClient client, PlayerEntity player, Hand hand, ItemStack stack) {
|
||||
if (!isRightClickPressed) {
|
||||
@ -107,7 +159,7 @@ public class AutoCrossbowHandler {
|
||||
return;
|
||||
}
|
||||
|
||||
if (CrossbowItem.isCharged(stack)) {
|
||||
if (isCrossbowEffectivelyCharged(stack)) {
|
||||
client.interactionManager.interactItem(player, hand);
|
||||
player.swingHand(hand);
|
||||
lastActionTime = System.currentTimeMillis();
|
||||
@ -118,7 +170,7 @@ public class AutoCrossbowHandler {
|
||||
swapToSlot(client, player, nextSlot);
|
||||
} else {
|
||||
player.sendMessage(Text.translatable("branulf_toolbox.autocrossbow.no_more_charged"), true);
|
||||
config.currentMode = AutoCrossbowMode.NORMAL;
|
||||
// config.currentMode = AutoCrossbowMode.NORMAL;
|
||||
Main.saveConfig();
|
||||
}
|
||||
}
|
||||
@ -142,11 +194,11 @@ public class AutoCrossbowHandler {
|
||||
swapToSlot(client, player, nextSlot);
|
||||
} else {
|
||||
player.sendMessage(Text.translatable("branulf_toolbox.autocrossbow.no_more_uncharged"), true);
|
||||
config.currentMode = AutoCrossbowMode.NORMAL;
|
||||
// config.currentMode = AutoCrossbowMode.NORMAL;
|
||||
Main.saveConfig();
|
||||
}
|
||||
}
|
||||
} else if (!CrossbowItem.isCharged(stack)) {
|
||||
} else if (!isCrossbowEffectivelyCharged(stack)) {
|
||||
if (System.currentTimeMillis() - lastActionTime < ACTION_COOLDOWN_MS) {
|
||||
return;
|
||||
}
|
||||
@ -163,7 +215,7 @@ public class AutoCrossbowHandler {
|
||||
lastActionTime = System.currentTimeMillis();
|
||||
} else {
|
||||
player.sendMessage(Text.translatable("branulf_toolbox.autocrossbow.no_more_uncharged"), true);
|
||||
config.currentMode = AutoCrossbowMode.NORMAL;
|
||||
// config.currentMode = AutoCrossbowMode.NORMAL;
|
||||
Main.saveConfig();
|
||||
}
|
||||
}
|
||||
@ -174,7 +226,7 @@ public class AutoCrossbowHandler {
|
||||
if (i == currentSelectedSlot) continue;
|
||||
ItemStack stack = inventory.getStack(i);
|
||||
if (stack.getItem() instanceof CrossbowItem) {
|
||||
boolean isCharged = CrossbowItem.isCharged(stack);
|
||||
boolean isCharged = isCrossbowEffectivelyCharged(stack);
|
||||
if (chargedRequired == isCharged) {
|
||||
return i;
|
||||
}
|
||||
@ -183,7 +235,7 @@ public class AutoCrossbowHandler {
|
||||
for (int i = PlayerInventory.HOTBAR_SIZE; i < inventory.main.size(); i++) {
|
||||
ItemStack stack = inventory.getStack(i);
|
||||
if (stack.getItem() instanceof CrossbowItem) {
|
||||
boolean isCharged = CrossbowItem.isCharged(stack);
|
||||
boolean isCharged = isCrossbowEffectivelyCharged(stack);
|
||||
if (chargedRequired == isCharged) {
|
||||
return i;
|
||||
}
|
||||
|
@ -5,12 +5,19 @@ import org.branulf.toolbox.ModConfig;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.text.Text;
|
||||
|
||||
|
||||
public class CrazyLookHandler {
|
||||
private ModConfig.CrazyLookConfig config;
|
||||
private int shakeCounter = 0;
|
||||
|
||||
/**
|
||||
* 你来这里干啥?
|
||||
* 我随便写的,我记得那天喝多了。
|
||||
*/
|
||||
|
||||
public CrazyLookHandler(ModConfig mainConfig) {
|
||||
this.config = mainConfig.crazyLook;
|
||||
Main.LOGGER.info("BRanulf的实用工具箱 - 夏季八看摇头晃脑挂机模块已加载!");
|
||||
}
|
||||
|
||||
public void onClientTick(MinecraftClient client) {
|
||||
|
@ -0,0 +1,410 @@
|
||||
package org.branulf.toolbox.damagelogger;
|
||||
|
||||
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
|
||||
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents;
|
||||
import net.fabricmc.fabric.api.entity.event.v1.ServerLivingEntityEvents;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.entity.damage.DamageSource;
|
||||
import net.minecraft.entity.damage.DamageTypes;
|
||||
import net.minecraft.entity.effect.StatusEffects;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.registry.tag.DamageTypeTags;
|
||||
import net.minecraft.registry.tag.FluidTags;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import org.branulf.toolbox.Main;
|
||||
import org.branulf.toolbox.ModConfig;
|
||||
|
||||
import java.awt.*;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
public class DamageLoggerHandler {
|
||||
private final ModConfig.DamageLoggerConfig config;
|
||||
private boolean isLogging = false;
|
||||
private String logFileName;
|
||||
private FileWriter csvWriter;
|
||||
private final Queue<DamageRecord> recordQueue = new ConcurrentLinkedQueue<>();
|
||||
|
||||
private float lastKnownHealth = -1.0F;
|
||||
|
||||
/**
|
||||
* 个人感觉,这里不需要进行本土化,所以这里直接使用中文。
|
||||
* 主要还是懒,不想加太多键,也不想让语言文件乱七八糟。
|
||||
* 部分方法使用了人工智能,因为我不喜欢繁琐的重复写这些乱七八糟的。
|
||||
* <p>
|
||||
* if ur Chinese not good, or u never learned Chinese, recommended to download Du####go.(or modify these method)
|
||||
*/
|
||||
|
||||
public DamageLoggerHandler(ModConfig.DamageLoggerConfig config) {
|
||||
this.config = config;
|
||||
setupEventListeners();
|
||||
Main.LOGGER.info("BRanulf的实用工具箱 - 伤害记录模块已加载!");
|
||||
}
|
||||
|
||||
private void setupEventListeners() {
|
||||
ServerLivingEntityEvents.AFTER_DAMAGE.register((entity, source, baseDamageTaken, damageTaken, blocked) -> {
|
||||
if (entity instanceof PlayerEntity player && player == MinecraftClient.getInstance().player && isLogging) {
|
||||
if (MinecraftClient.getInstance().isIntegratedServerRunning()) {
|
||||
float newHealth = player.getHealth();
|
||||
float originalHealth = newHealth + damageTaken;
|
||||
recordDamage(source, damageTaken, originalHealth, newHealth);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
ClientTickEvents.END_CLIENT_TICK.register(client -> {
|
||||
if (client.player == null) {
|
||||
lastKnownHealth = -1.0F;
|
||||
return;
|
||||
}
|
||||
|
||||
if (isLogging) {
|
||||
float currentHealth = client.player.getHealth();
|
||||
|
||||
if (lastKnownHealth == -1.0F) {
|
||||
lastKnownHealth = currentHealth;
|
||||
} else if (currentHealth < lastKnownHealth) {
|
||||
float damageAmount = lastKnownHealth - currentHealth;
|
||||
if (!client.isIntegratedServerRunning()) {
|
||||
recordClientSideDamage(damageAmount, lastKnownHealth, currentHealth);
|
||||
}
|
||||
lastKnownHealth = currentHealth;
|
||||
} else if (currentHealth > lastKnownHealth) {
|
||||
lastKnownHealth = currentHealth;
|
||||
}
|
||||
} else {
|
||||
lastKnownHealth = -1.0F;
|
||||
}
|
||||
|
||||
if (isLogging && !recordQueue.isEmpty()) {
|
||||
processRecordQueue();
|
||||
}
|
||||
});
|
||||
|
||||
ClientPlayConnectionEvents.DISCONNECT.register((handler, client) -> {
|
||||
if (isLogging) {
|
||||
stopLogging();
|
||||
if (!config.rememberState) {
|
||||
config.enabled = false;
|
||||
Main.saveConfig();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void startLogging() {
|
||||
if (isLogging) return;
|
||||
|
||||
MinecraftClient client = MinecraftClient.getInstance();
|
||||
if (client.player == null) {
|
||||
if (client.inGameHud != null) {
|
||||
client.inGameHud.getChatHud().addMessage(Text.literal("§c[伤害记录]§f 无法开始记录:玩家未加载或未进入世界。"));
|
||||
} else {
|
||||
System.err.println("[伤害记录] 无法开始记录:玩家未加载或未进入世界。");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!client.isIntegratedServerRunning()) {
|
||||
client.player.sendMessage(Text.literal("§e[伤害记录]§f 注意:此功能在专用服务器上只能通过客户端状态推断伤害,可能不完全准确。详细记录仅在单人/局域网游戏有效。"), false);
|
||||
}
|
||||
|
||||
try {
|
||||
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss");
|
||||
String timestamp = dateFormat.format(new Date());
|
||||
logFileName = "伤害记录_" + timestamp + ".csv";
|
||||
|
||||
Path logDir = Paths.get("damage_logs");
|
||||
if (!Files.exists(logDir)) {
|
||||
Files.createDirectories(logDir);
|
||||
}
|
||||
|
||||
Path logFile = logDir.resolve(logFileName);
|
||||
csvWriter = new FileWriter(logFile.toFile(), true);
|
||||
|
||||
csvWriter.write("时间,伤害来源,伤害类型,伤害数值,原血量,新血量,世界坐标,维度,原始伤害源数据\n");
|
||||
csvWriter.flush();
|
||||
|
||||
isLogging = true;
|
||||
lastKnownHealth = client.player.getHealth();
|
||||
client.player.sendMessage(Text.literal("§a[伤害记录]§f 已开始记录伤害。"), false);
|
||||
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
if (client.player != null) {
|
||||
client.player.sendMessage(Text.literal("§c[伤害记录]§f 启动失败: " + e.getMessage()), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void stopLogging() {
|
||||
if (!isLogging) return;
|
||||
|
||||
if (csvWriter != null) {
|
||||
try {
|
||||
processRecordQueue();
|
||||
csvWriter.close();
|
||||
csvWriter = null;
|
||||
if (MinecraftClient.getInstance().player != null) {
|
||||
MinecraftClient.getInstance().player.sendMessage(Text.literal("§a[伤害记录]§f 已停止记录伤害,日志已保存。"), false);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
if (MinecraftClient.getInstance().player != null) {
|
||||
MinecraftClient.getInstance().player.sendMessage(Text.literal("§c[伤害记录]§f 停止失败: " + e.getMessage()), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
isLogging = false;
|
||||
lastKnownHealth = -1.0F;
|
||||
}
|
||||
|
||||
private void recordDamage(DamageSource source, float amount, float originalHealth, float newHealth) {
|
||||
if (MinecraftClient.getInstance().player == null) return;
|
||||
|
||||
String sourceName = getSourceName(source);
|
||||
String damageType = getDamageType(source);
|
||||
String rawSourceData = source.toString();
|
||||
|
||||
DamageRecord record = new DamageRecord(
|
||||
new Date(),
|
||||
sourceName,
|
||||
damageType,
|
||||
amount,
|
||||
originalHealth,
|
||||
newHealth,
|
||||
MinecraftClient.getInstance().player.getBlockPos(),
|
||||
MinecraftClient.getInstance().player.getWorld().getRegistryKey().getValue().toString(),
|
||||
rawSourceData
|
||||
);
|
||||
recordQueue.add(record);
|
||||
}
|
||||
|
||||
private void recordClientSideDamage(float amount, float originalHealth, float newHealth) {
|
||||
MinecraftClient client = MinecraftClient.getInstance();
|
||||
if (client.player == null) return;
|
||||
|
||||
String sourceName = "未知来源 (客户端推断)";
|
||||
String damageType = inferClientSideDamageType(client.player);
|
||||
String rawSourceData = "客户端推断伤害,无原始DamageSource数据";
|
||||
|
||||
if (client.player.getLastAttacker() != null && client.player.age - client.player.getLastAttackedTime() < 20) {
|
||||
sourceName = client.player.getLastAttacker().getName().getString() + " (客户端推断)";
|
||||
damageType = "攻击伤害 (客户端推断)";
|
||||
}
|
||||
|
||||
DamageRecord record = new DamageRecord(
|
||||
new Date(),
|
||||
sourceName,
|
||||
damageType,
|
||||
amount,
|
||||
originalHealth,
|
||||
newHealth,
|
||||
client.player.getBlockPos(),
|
||||
client.player.getWorld().getRegistryKey().getValue().toString(),
|
||||
rawSourceData
|
||||
);
|
||||
recordQueue.add(record);
|
||||
}
|
||||
|
||||
private void processRecordQueue() {
|
||||
try {
|
||||
while (!recordQueue.isEmpty()) {
|
||||
DamageRecord record = recordQueue.poll();
|
||||
if (record != null) {
|
||||
String csvLine = String.format("%s,%s,%s,%.1f,%.1f,%.1f,%s,%s,%s\n",
|
||||
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(record.timestamp),
|
||||
escapeCsv(record.sourceName),
|
||||
escapeCsv(record.damageType),
|
||||
record.amount,
|
||||
record.originalHealth,
|
||||
record.newHealth,
|
||||
escapeCsv(record.position.toString()),
|
||||
escapeCsv(record.dimension),
|
||||
escapeCsv(record.rawDamageSourceData)
|
||||
);
|
||||
csvWriter.write(csvLine);
|
||||
}
|
||||
}
|
||||
csvWriter.flush();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
if (MinecraftClient.getInstance().player != null) {
|
||||
MinecraftClient.getInstance().player.sendMessage(Text.literal("§c[伤害记录]§f 写入日志失败: " + e.getMessage()), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String escapeCsv(String input) {
|
||||
if (input == null) return "";
|
||||
if (input.contains(",") || input.contains("\"") || input.contains("\n")) {
|
||||
return "\"" + input.replace("\"", "\"\"") + "\"";
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
||||
public boolean isLogging() {
|
||||
return isLogging;
|
||||
}
|
||||
|
||||
public void openLogFolder() {
|
||||
Path logDir = Paths.get("damage_logs").toAbsolutePath();
|
||||
MinecraftClient client = MinecraftClient.getInstance();
|
||||
|
||||
try {
|
||||
if (!Files.exists(logDir)) {
|
||||
Files.createDirectories(logDir);
|
||||
}
|
||||
|
||||
boolean opened = false;
|
||||
|
||||
try {
|
||||
if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.OPEN)) {
|
||||
Desktop.getDesktop().open(logDir.toFile());
|
||||
opened = true;
|
||||
}
|
||||
} catch (HeadlessException e) {
|
||||
System.err.println("[伤害记录] 桌面API报告了无头环境。尝试替代方法。");
|
||||
} catch (IOException | SecurityException e) {
|
||||
e.printStackTrace();
|
||||
if (client.player != null) {
|
||||
client.player.sendMessage(Text.literal("§c[伤害记录]§f 尝试使用Java Desktop API打开日志文件夹失败: " + e.getMessage()), false);
|
||||
}
|
||||
}
|
||||
|
||||
if (!opened && System.getProperty("os.name").toLowerCase().contains("win")) {
|
||||
try {
|
||||
String command = "cmd /c start \"\" \"" + logDir.toAbsolutePath().toString() + "\"";
|
||||
Process process = Runtime.getRuntime().exec(command);
|
||||
opened = true;
|
||||
if (client.player != null) {
|
||||
client.player.sendMessage(Text.literal("§a[伤害记录]§f 已尝试使用Windows命令打开日志文件夹。"), false);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
if (client.player != null) {
|
||||
client.player.sendMessage(Text.literal("§c[伤害记录]§f 尝试使用Windows命令打开日志文件夹失败: " + e.getMessage()), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!opened) {
|
||||
if (client.player != null) {
|
||||
client.player.sendMessage(Text.literal("§e[伤害记录]§f 自动打开日志文件夹失败。请手动前往: " + logDir.toAbsolutePath()), false);
|
||||
}
|
||||
System.err.println("[伤害记录] 无法自动打开日志文件夹。请手动打开: " + logDir.toAbsolutePath());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
if (client.player != null) {
|
||||
client.player.sendMessage(Text.literal("§c[伤害记录]§f 创建日志文件夹失败: " + e.getMessage()), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String getSourceName(DamageSource source) {
|
||||
if (source.getAttacker() != null) {
|
||||
return source.getAttacker().getName().getString();
|
||||
}
|
||||
return source.getName();
|
||||
}
|
||||
|
||||
private String getDamageType(DamageSource source) {
|
||||
if (source.isOf(DamageTypes.OUT_OF_WORLD)) return "虚空伤害";
|
||||
if (source.isOf(DamageTypes.BAD_RESPAWN_POINT)) return "错误重生点爆炸伤害";
|
||||
if (source.isOf(DamageTypes.SONIC_BOOM)) return "音爆伤害";
|
||||
if (source.isOf(DamageTypes.MACE_SMASH)) return "狼牙棒猛击伤害";
|
||||
if (source.isOf(DamageTypes.WITHER)) return "凋零伤害";
|
||||
if (source.isOf(DamageTypes.MAGIC) || source.isOf(DamageTypes.INDIRECT_MAGIC)) return "魔法伤害";
|
||||
if (source.isOf(DamageTypes.DRAGON_BREATH)) return "龙息伤害";
|
||||
if (source.isOf(DamageTypes.STARVE)) return "饥饿伤害";
|
||||
if (source.isOf(DamageTypes.DROWN)) return "溺水伤害";
|
||||
if (source.isOf(DamageTypes.FREEZE)) return "冰冻伤害";
|
||||
if (source.isOf(DamageTypes.LIGHTNING_BOLT)) return "闪电伤害";
|
||||
if (source.isOf(DamageTypes.THORNS)) return "荆棘伤害";
|
||||
if (source.isOf(DamageTypes.CACTUS)) return "仙人掌伤害";
|
||||
if (source.isOf(DamageTypes.SWEET_BERRY_BUSH)) return "甜浆果丛伤害";
|
||||
if (source.isOf(DamageTypes.STALAGMITE)) return "钟乳石伤害";
|
||||
if (source.isOf(DamageTypes.FLY_INTO_WALL)) return "撞墙伤害";
|
||||
if (source.isOf(DamageTypes.IN_WALL)) return "窒息伤害 (卡墙)";
|
||||
if (source.isOf(DamageTypes.CRAMMING)) return "挤压伤害";
|
||||
if (source.isOf(DamageTypes.DRY_OUT)) return "脱水伤害";
|
||||
if (source.isOf(DamageTypes.HOT_FLOOR)) return "热地板伤害";
|
||||
if (source.isOf(DamageTypes.FALLING_ANVIL) || source.isOf(DamageTypes.FALLING_STALACTITE) || source.isOf(DamageTypes.FALLING_BLOCK)) return "掉落方块伤害";
|
||||
|
||||
if (source.isOf(DamageTypes.IN_FIRE) || source.isOf(DamageTypes.ON_FIRE) || source.isOf(DamageTypes.LAVA) || source.isOf(DamageTypes.CAMPFIRE) || source.isOf(DamageTypes.FIREBALL) || source.isOf(DamageTypes.UNATTRIBUTED_FIREBALL)) return "火焰/燃烧伤害";
|
||||
|
||||
if (source.isOf(DamageTypes.ARROW) || source.isOf(DamageTypes.TRIDENT) || source.isOf(DamageTypes.MOB_PROJECTILE) ||
|
||||
source.isOf(DamageTypes.SPIT) || source.isOf(DamageTypes.WIND_CHARGE) || source.isOf(DamageTypes.FIREWORKS) ||
|
||||
source.isOf(DamageTypes.WITHER_SKULL) || source.isOf(DamageTypes.THROWN) || source.isOf(DamageTypes.ENDER_PEARL)) return "弹射物伤害";
|
||||
|
||||
if (source.isOf(DamageTypes.EXPLOSION) || source.isOf(DamageTypes.PLAYER_EXPLOSION)) return "爆炸伤害";
|
||||
|
||||
if (source.isOf(DamageTypes.MOB_ATTACK) || source.isOf(DamageTypes.PLAYER_ATTACK) || source.isOf(DamageTypes.MOB_ATTACK_NO_AGGRO) || source.isOf(DamageTypes.STING)) return "近战/攻击伤害";
|
||||
|
||||
if (source.isIn(DamageTypeTags.IS_PROJECTILE)) return "弹射物伤害 (通用)";
|
||||
if (source.isIn(DamageTypeTags.IS_EXPLOSION)) return "爆炸伤害 (通用)";
|
||||
if (source.isIn(DamageTypeTags.IS_FIRE)) return "火焰伤害 (通用)";
|
||||
if (source.isIn(DamageTypeTags.IS_FALL)) return "坠落伤害";
|
||||
if (source.isIn(DamageTypeTags.IS_DROWNING)) return "溺水伤害 (通用)";
|
||||
if (source.isIn(DamageTypeTags.IS_FREEZING)) return "冰冻伤害 (通用)";
|
||||
if (source.isIn(DamageTypeTags.IS_LIGHTNING)) return "闪电伤害 (通用)";
|
||||
if (source.isIn(DamageTypeTags.IS_PLAYER_ATTACK)) return "玩家攻击伤害";
|
||||
if (source.isIn(DamageTypeTags.MACE_SMASH)) return "狼牙棒猛击伤害 (通用)";
|
||||
|
||||
if (source.isOf(DamageTypes.GENERIC)) return "环境伤害 (通用)";
|
||||
if (source.isOf(DamageTypes.GENERIC_KILL)) return "通用击杀伤害";
|
||||
if (source.isOf(DamageTypes.OUTSIDE_BORDER)) return "世界边界伤害";
|
||||
if (source.isOf(DamageTypes.FALL)) return "坠落伤害 (通用)";
|
||||
|
||||
if (source.getAttacker() != null) {
|
||||
return "攻击伤害 (未分类)";
|
||||
}
|
||||
|
||||
return "其他伤害 (" + source.getName() + ")";
|
||||
}
|
||||
|
||||
private String inferClientSideDamageType(PlayerEntity player) {
|
||||
if (player.isOnFire()) return "火焰/燃烧伤害 (推断)";
|
||||
if (player.isInLava()) return "岩浆伤害 (推断)";
|
||||
if (player.isSubmergedIn(FluidTags.WATER) && player.getAir() == 0) return "溺水伤害 (推断)";
|
||||
if (player.hasStatusEffect(StatusEffects.POISON)) return "中毒伤害 (推断)";
|
||||
if (player.hasStatusEffect(StatusEffects.WITHER)) return "凋零伤害 (推断)";
|
||||
|
||||
return "未知伤害 (客户端推断)";
|
||||
}
|
||||
|
||||
private static class DamageRecord {
|
||||
Date timestamp;
|
||||
String sourceName;
|
||||
String damageType;
|
||||
float amount;
|
||||
float originalHealth;
|
||||
float newHealth;
|
||||
BlockPos position;
|
||||
String dimension;
|
||||
String rawDamageSourceData;
|
||||
|
||||
public DamageRecord(Date timestamp, String sourceName, String damageType, float amount,
|
||||
float originalHealth, float newHealth,
|
||||
BlockPos position, String dimension, String rawDamageSourceData) {
|
||||
this.timestamp = timestamp;
|
||||
this.sourceName = sourceName;
|
||||
this.damageType = damageType;
|
||||
this.amount = amount;
|
||||
this.originalHealth = originalHealth;
|
||||
this.newHealth = newHealth;
|
||||
this.position = position;
|
||||
this.dimension = dimension;
|
||||
this.rawDamageSourceData = rawDamageSourceData;
|
||||
}
|
||||
}
|
||||
}
|
@ -2,15 +2,11 @@ package org.branulf.toolbox.elytraboost;
|
||||
|
||||
import org.branulf.toolbox.Main;
|
||||
import net.fabricmc.api.ModInitializer;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class ElytraBoostCommon implements ModInitializer {
|
||||
public static final String MOD_ID = Main.MOD_ID;
|
||||
public static final Logger LOGGER = LoggerFactory.getLogger(MOD_ID);
|
||||
|
||||
@Override
|
||||
public void onInitialize() {
|
||||
LOGGER.info("BRanulf的实用工具箱 - 鞘翅推进优化模块已加载!");
|
||||
Main.LOGGER.info("BRanulf的实用工具箱 - 鞘翅推进优化模块已加载!");
|
||||
}
|
||||
}
|
||||
|
@ -6,14 +6,20 @@ import net.minecraft.sound.SoundCategory;
|
||||
import net.minecraft.sound.SoundEvents;
|
||||
import net.minecraft.util.math.Box;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import org.branulf.toolbox.Main;
|
||||
import org.branulf.toolbox.ModConfig;
|
||||
|
||||
public class PlayerDetectorHandler {
|
||||
private final ModConfig config;
|
||||
private int ticksSinceLastAlert = 0;
|
||||
|
||||
/**
|
||||
* 我已经不止一次在服务器挂机被偷屁股了。
|
||||
*/
|
||||
|
||||
public PlayerDetectorHandler(ModConfig config) {
|
||||
this.config = config;
|
||||
Main.LOGGER.info("BRanulf的实用工具箱 - 玩家检测模块已加载!");
|
||||
}
|
||||
|
||||
public void onClientTick(MinecraftClient client) {
|
||||
|
@ -10,7 +10,7 @@ import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
public class ScrafitiptyPlusDebug {
|
||||
private static Logger logger = LogManager.getLogger(Main.MOD_ID);
|
||||
public static Logger logger = LogManager.getLogger(Main.MOD_ID);
|
||||
|
||||
public static void setLogger(String modId) {
|
||||
logger = LogManager.getLogger(modId);
|
||||
|
@ -1,5 +1,7 @@
|
||||
package org.branulf.toolbox.scrafitiptyplus;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.branulf.toolbox.Main;
|
||||
import org.branulf.toolbox.ModConfig;
|
||||
import com.google.gson.JsonObject;
|
||||
@ -23,6 +25,7 @@ import java.util.*;
|
||||
|
||||
public class ScrafitiptyPlusHandler {
|
||||
public static final String MOD_ID = Main.MOD_ID;
|
||||
private static Logger LOGGER = ScrafitiptyPlusDebug.logger;
|
||||
private ModConfig.ScrafitiptyPlusConfigPart config;
|
||||
|
||||
private ScriptWebSocketServer webSocketServerInstance;
|
||||
@ -35,11 +38,13 @@ public class ScrafitiptyPlusHandler {
|
||||
public ScrafitiptyPlusHandler(ModConfig mainConfig) {
|
||||
this.config = mainConfig.scrafitiptyPlus;
|
||||
ScrafitiptyPlusDebug.setLogger(Main.MOD_ID);
|
||||
ScrafitiptyPlusDebug.info("Scrafitipty Plus Mod 正在初始化...");
|
||||
ScrafitiptyPlusDebug.info("Scrafitipty Plus 模块正在初始化...");
|
||||
|
||||
if (this.config.enableMode == ModConfig.ScrafitiptyPlusConfigPart.WebSocketEnableMode.ALWAYS) {
|
||||
startWebSocketServer();
|
||||
}
|
||||
|
||||
LOGGER.info("BRanulf的实用工具箱 - Scrafitipty Plus(Websocket控制)模块已加载!");
|
||||
}
|
||||
|
||||
public boolean isServerRunning() {
|
||||
|
@ -6,12 +6,15 @@
|
||||
"branulf_toolbox.enabled": "Enabled",
|
||||
"branulf_toolbox.disabled": "Disabled",
|
||||
|
||||
"branulf_toolbox.commit": "im really frustrated, why i cant use comments on JSON!",
|
||||
|
||||
"text.autoconfig.branulf_toolbox.title": "BRanulf's Toolbox Configuration",
|
||||
"text.autoconfig.branulf_toolbox.category.autocrossbow_settings": "Auto Crossbow Settings",
|
||||
"text.autoconfig.branulf_toolbox.category.scrafitipty_plus_settings": "Scrafitipty Plus Settings",
|
||||
"text.autoconfig.branulf_toolbox.category.crazylook_settings": "Crazy Look Settings",
|
||||
"text.autoconfig.branulf_toolbox.category.elytraboost_settings": "Elytra Boost Settings",
|
||||
"text.autoconfig.branulf_toolbox.category.player_detector_settings": "Player Detector Settings",
|
||||
"text.autoconfig.branulf_toolbox.category.damage_logger_settings": "Damage Logger Settings",
|
||||
|
||||
"gui.branulf_toolbox.autocrossbow.title": "Auto Crossbow",
|
||||
"gui.branulf_toolbox.autocrossbow.enabled_on": "Enable Auto Crossbow",
|
||||
@ -29,6 +32,10 @@
|
||||
"branulf_toolbox.autocrossbow.command.invalid_mode": "Invalid mode: '%s'. Available modes: normal, rapid_shoot, rapid_load.",
|
||||
"text.autoconfig.branulf_toolbox.option.autocrossbow.enabled": "Enable Auto Crossbow",
|
||||
"text.autoconfig.branulf_toolbox.option.autocrossbow.currentMode": "Current Mode",
|
||||
"text.autoconfig.branulf_toolbox.option.autocrossbow.compatibilityMode": "Compatibility Mode",
|
||||
"text.autoconfig.branulf_toolbox.option.autocrossbow.compatibilityMode.@Tooltip": "If enabled, a crossbow with any projectile loaded will be treated as charged, regardless of its visual state. Useful for servers where loaded crossbows don't visually update.",
|
||||
"text.autoconfig.branulf_toolbox.option.autocrossbow.ultimateCompatibilityMode": "Ultimate Compatibility Mode (Normal Mode Only)",
|
||||
"text.autoconfig.branulf_toolbox.option.autocrossbow.ultimateCompatibilityMode.@Tooltip": "Overrides all other crossbow charge checks in Normal Mode. If charging is complete, it's considered charged. If just fired, it's uncharged. Ignores actual item state.",
|
||||
|
||||
"gui.branulf_toolbox.scrafitipty_plus.title": "Scrafitipty Plus (WebSocket)",
|
||||
"gui.branulf_toolbox.scrafitipty_plus.toggle_server": "Toggle WebSocket Server",
|
||||
@ -77,5 +84,13 @@
|
||||
"text.autoconfig.branulf_toolbox.option.playerDetector.detectionRange": "Detection Range (Blocks)",
|
||||
"text.autoconfig.branulf_toolbox.option.playerDetector.detectionRange.@Tooltip": "The radius in blocks to detect other players (1-64).",
|
||||
"text.autoconfig.branulf_toolbox.option.playerDetector.alertFrequency": "Alert Frequency (times/sec)",
|
||||
"text.autoconfig.branulf_toolbox.option.playerDetector.alertFrequency.@Tooltip": "How many times per second to play the alert sound when a player is detected (1-20)."
|
||||
"text.autoconfig.branulf_toolbox.option.playerDetector.alertFrequency.@Tooltip": "How many times per second to play the alert sound when a player is detected (1-20).",
|
||||
|
||||
"gui.branulf_toolbox.damage_logger.title": "Damage Logger",
|
||||
"gui.branulf_toolbox.damage_logger.enable": "Enable Damage Logger",
|
||||
"gui.branulf_toolbox.damage_logger.disable": "Disable Damage Logger",
|
||||
"gui.branulf_toolbox.damage_logger.open_folder": "Open Log Folder",
|
||||
"text.autoconfig.branulf_toolbox.option.damageLogger.enabled": "Enable Damage Logger",
|
||||
"text.autoconfig.branulf_toolbox.option.damageLogger.rememberState": "Remember Logging State",
|
||||
"text.autoconfig.branulf_toolbox.option.damageLogger.rememberState.@Tooltip": "If enabled, the damage logger will resume logging automatically when you restart the game if it was active. If disabled, it will turn off on game exit."
|
||||
}
|
||||
|
@ -6,12 +6,15 @@
|
||||
"branulf_toolbox.enabled": "已启用",
|
||||
"branulf_toolbox.disabled": "已禁用",
|
||||
|
||||
"branulf_toolbox.commit": "真的服了,为毛JSON不让我使用注释!",
|
||||
|
||||
"text.autoconfig.branulf_toolbox.title": "BRanulf的实用工具箱配置",
|
||||
"text.autoconfig.branulf_toolbox.category.autocrossbow_settings": "诸葛连弩设置",
|
||||
"text.autoconfig.branulf_toolbox.category.scrafitipty_plus_settings": "Scrafitipty Plus 设置",
|
||||
"text.autoconfig.branulf_toolbox.category.crazylook_settings": "疯狂视角设置",
|
||||
"text.autoconfig.branulf_toolbox.category.crazylook_settings": "摇晃挂机设置",
|
||||
"text.autoconfig.branulf_toolbox.category.elytraboost_settings": "鞘翅推进优化设置",
|
||||
"text.autoconfig.branulf_toolbox.category.player_detector_settings": "玩家探测器设置",
|
||||
"text.autoconfig.branulf_toolbox.category.damage_logger_settings": "伤害记录设置",
|
||||
|
||||
"gui.branulf_toolbox.autocrossbow.title": "诸葛连弩",
|
||||
"gui.branulf_toolbox.autocrossbow.enabled_on": "启用诸葛连弩",
|
||||
@ -29,6 +32,10 @@
|
||||
"branulf_toolbox.autocrossbow.command.invalid_mode": "无效模式:“%s”。可用模式:normal, rapid_shoot, rapid_load。",
|
||||
"text.autoconfig.branulf_toolbox.option.autocrossbow.enabled": "启用诸葛连弩",
|
||||
"text.autoconfig.branulf_toolbox.option.autocrossbow.currentMode": "当前模式",
|
||||
"text.autoconfig.branulf_toolbox.option.autocrossbow.compatibilityMode": "兼容模式",
|
||||
"text.autoconfig.branulf_toolbox.option.autocrossbow.compatibilityMode.@Tooltip": "启用时,只要弩有填充物(无论是否已装填完毕),就判断为已装填好的弩。适用于服务器插件导致已装填弩外观不改变的情况。",
|
||||
"text.autoconfig.branulf_toolbox.option.autocrossbow.ultimateCompatibilityMode": "最终兼容模式 (仅正常模式)",
|
||||
"text.autoconfig.branulf_toolbox.option.autocrossbow.ultimateCompatibilityMode.@Tooltip": "在正常模式下,此选项会覆盖所有其他弩装填状态检查。如果蓄力完成,则视为已装填;如果刚射击过,则视为未装填。不依赖物品实际状态。",
|
||||
|
||||
"gui.branulf_toolbox.scrafitipty_plus.title": "Scrafitipty Plus (WebSocket)",
|
||||
"gui.branulf_toolbox.scrafitipty_plus.toggle_server": "切换WebSocket服务器",
|
||||
@ -77,5 +84,13 @@
|
||||
"text.autoconfig.branulf_toolbox.option.playerDetector.detectionRange": "探测范围(方块)",
|
||||
"text.autoconfig.branulf_toolbox.option.playerDetector.detectionRange.@Tooltip": "探测其他玩家的半径范围(1-64方块)。",
|
||||
"text.autoconfig.branulf_toolbox.option.playerDetector.alertFrequency": "提示频率(次/秒)",
|
||||
"text.autoconfig.branulf_toolbox.option.playerDetector.alertFrequency.@Tooltip": "当探测到玩家时,每秒播放提示音的次数(1-20)。"
|
||||
"text.autoconfig.branulf_toolbox.option.playerDetector.alertFrequency.@Tooltip": "当探测到玩家时,每秒播放提示音的次数(1-20)。",
|
||||
|
||||
"gui.branulf_toolbox.damage_logger.title": "伤害记录",
|
||||
"gui.branulf_toolbox.damage_logger.enable": "启用伤害记录",
|
||||
"gui.branulf_toolbox.damage_logger.disable": "禁用伤害记录",
|
||||
"gui.branulf_toolbox.damage_logger.open_folder": "打开日志文件夹",
|
||||
"text.autoconfig.branulf_toolbox.option.damageLogger.enabled": "启用伤害记录",
|
||||
"text.autoconfig.branulf_toolbox.option.damageLogger.rememberState": "记住开关状态",
|
||||
"text.autoconfig.branulf_toolbox.option.damageLogger.rememberState.@Tooltip": "如果启用,当您退出游戏时,伤害记录功能将保存当前状态并在下次进入游戏时自动恢复。如果禁用,退出游戏后将关闭伤害记录功能。"
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user