feat: norp系统和配置
This commit is contained in:
parent
81dbc80a45
commit
cd5689b4a6
|
|
@ -19,13 +19,16 @@ package top.r3944realms.eroticdungeongame;
|
|||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraftforge.eventbus.api.IEventBus;
|
||||
import net.minecraftforge.fml.ModList;
|
||||
import net.minecraftforge.fml.ModLoadingContext;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.config.ModConfig;
|
||||
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import software.bernie.geckolib.GeckoLib;
|
||||
import top.r3944realms.eroticdungeongame.config.EDGConfig;
|
||||
import top.r3944realms.eroticdungeongame.content.recipe.EDGRecipeBookTypes;
|
||||
import top.r3944realms.eroticdungeongame.content.recipe.EDGRecipeTypeCategories;
|
||||
import top.r3944realms.eroticdungeongame.core.network.EDGNetworkHandler;
|
||||
|
|
@ -58,6 +61,7 @@ public class EroticDungeon {
|
|||
EDGRecipeBookTypes.init();
|
||||
EDGRecipeTypeCategories.init();
|
||||
EDGNetworkHandler.register();
|
||||
ModLoadingContext.get().registerConfig(ModConfig.Type.COMMON, EDGConfig.SPEC);
|
||||
}
|
||||
@Contract("_ -> new")
|
||||
public static @NotNull ResourceLocation rl(String path) {
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ package top.r3944realms.eroticdungeongame.api.capability;
|
|||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import org.intellij.lang.annotations.MagicConstant;
|
||||
|
||||
/**
|
||||
* The interface Player dungeon data.
|
||||
|
|
@ -79,4 +80,43 @@ public interface IPlayerDungeonData {
|
|||
* @param eyeHeight the eye height
|
||||
*/
|
||||
void setEyeHeight(float eyeHeight);
|
||||
|
||||
int INVALID = -1;
|
||||
/**
|
||||
* The constant IDLE.
|
||||
*/
|
||||
int IDLE = 0;
|
||||
/**
|
||||
* The constant REQUEST.
|
||||
*/
|
||||
int REQUEST = 1;
|
||||
|
||||
|
||||
/**
|
||||
* Sets norp state.
|
||||
*
|
||||
* @param state the state
|
||||
*/
|
||||
void setNorpState(@MagicConstant(intValues = {INVALID, IDLE, REQUEST}) int state);
|
||||
|
||||
/**
|
||||
* Gets norp state.
|
||||
*
|
||||
* @return the norp state
|
||||
*/
|
||||
int getNorpState();
|
||||
|
||||
/**
|
||||
* Sets norp time.
|
||||
*
|
||||
* @param norpTime the norp time
|
||||
*/
|
||||
void setNorpTime(long norpTime);
|
||||
|
||||
/**
|
||||
* Gets norp time.
|
||||
*
|
||||
* @return the norp time
|
||||
*/
|
||||
long getNorpTime();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,158 @@
|
|||
/*
|
||||
* Copyright 2025-2026 R3944Realms
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.r3944realms.eroticdungeongame.config;
|
||||
|
||||
import net.minecraftforge.common.ForgeConfigSpec;
|
||||
|
||||
public class EDGConfig {
|
||||
//todo: 配置
|
||||
// 是否开启norp、
|
||||
// norp间隔时间、
|
||||
// norp成功后冷却时间、
|
||||
// 爱机是否开启伤害
|
||||
// 爱机伤害系数
|
||||
public static final ForgeConfigSpec.Builder BUILDER = new ForgeConfigSpec.Builder();
|
||||
/**
|
||||
* The constant SPEC.
|
||||
*/
|
||||
public static final ForgeConfigSpec SPEC;
|
||||
/**
|
||||
* The constant COMMON.
|
||||
*/
|
||||
public static final Common COMMON;
|
||||
static {
|
||||
BUILDER.comment("EroticDungeon Common Configuration / 通用配置").push("EroticDungeon");
|
||||
|
||||
COMMON = new Common(BUILDER);
|
||||
|
||||
BUILDER.pop();
|
||||
SPEC = BUILDER.build();
|
||||
}
|
||||
|
||||
public static class Common {
|
||||
|
||||
// NORP相关配置
|
||||
public final ForgeConfigSpec.BooleanValue ENABLE_NORP;
|
||||
public final ForgeConfigSpec.IntValue NORP_INTERVAL_TIME;
|
||||
public final ForgeConfigSpec.IntValue NORP_SUCCESSFUL_COOLDOWN;
|
||||
|
||||
// 爱机相关配置
|
||||
public final ForgeConfigSpec.BooleanValue ENABLE_LOVE_MACHINE_DAMAGE;
|
||||
public final ForgeConfigSpec.DoubleValue LOVE_MACHINE_DAMAGE_FACTOR;
|
||||
|
||||
public Common(ForgeConfigSpec.Builder builder) {
|
||||
|
||||
// NORP配置组 - 中英文说明
|
||||
builder.comment(
|
||||
"NORP (No Role Play) Mode Settings / NORP(非角色扮演)模式设置",
|
||||
"----------------------------------------",
|
||||
"EN: Settings related to No Role Play mode functionality",
|
||||
"CN: 与非角色扮演模式功能相关的设置"
|
||||
).push("norp_settings");
|
||||
|
||||
ENABLE_NORP = builder
|
||||
.comment(
|
||||
"EN: Enable No Role Play mode",
|
||||
" If set to true, NORP mode will be enabled",
|
||||
" Default: true",
|
||||
"",
|
||||
"CN: 启用非角色扮演模式",
|
||||
" 如果设为true,将启用NORP模式",
|
||||
" 默认值: true"
|
||||
)
|
||||
.define("enableNorp", true);
|
||||
|
||||
NORP_INTERVAL_TIME = builder
|
||||
.comment(
|
||||
"EN: NORP interval time (in seconds)",
|
||||
" Time between NORP interactions",
|
||||
" Default: 30 seconds",
|
||||
" Min: 5, Max: 3600",
|
||||
"",
|
||||
"CN: NORP间隔时间(秒)",
|
||||
" NORP交互之间的间隔时间",
|
||||
" 默认值: 30秒",
|
||||
" 最小值: 5, 最大值: 3600"
|
||||
)
|
||||
.defineInRange("norpIntervalTime", 30, 5, 3600);
|
||||
|
||||
NORP_SUCCESSFUL_COOLDOWN = builder
|
||||
.comment(
|
||||
"EN: NORP cooldown time after successful interaction (in seconds)",
|
||||
" Time before another NORP interaction can occur",
|
||||
" Default: 60 seconds",
|
||||
" Min: 10, Max: 7200",
|
||||
"",
|
||||
"CN: NORP成功交互后的冷却时间(秒)",
|
||||
" 进行下一次NORP交互前需要等待的时间",
|
||||
" 默认值: 60秒",
|
||||
" 最小值: 10, 最大值: 7200"
|
||||
)
|
||||
.defineInRange("norpSuccessfulCooldown", 60, 10, 7200);
|
||||
|
||||
builder.pop(); // 退出NORP配置组
|
||||
|
||||
// 爱机配置组 - 中英文说明
|
||||
builder.comment(
|
||||
"Love Machine Settings / 爱机设置",
|
||||
"----------------------------------------",
|
||||
"EN: Settings related to Love Machine functionality",
|
||||
"CN: 与爱机功能相关的设置"
|
||||
).push("love_machine_settings");
|
||||
|
||||
ENABLE_LOVE_MACHINE_DAMAGE = builder
|
||||
.comment(
|
||||
"EN: Enable Love Machine damage",
|
||||
" If set to true, love machines can deal damage",
|
||||
" Default: false",
|
||||
"",
|
||||
"CN: 启用爱机伤害",
|
||||
" 如果设为true,爱机可以造成伤害",
|
||||
" 默认值: false"
|
||||
)
|
||||
.define("enableLoveMachineDamage", false);
|
||||
|
||||
LOVE_MACHINE_DAMAGE_FACTOR = builder
|
||||
.comment(
|
||||
"EN: Love Machine damage factor",
|
||||
" Damage multiplier for love machines",
|
||||
" Default: 1.0",
|
||||
" Min: 0.1, Max: 10.0",
|
||||
"",
|
||||
"CN: 爱机伤害系数",
|
||||
" 爱机的伤害倍率",
|
||||
" 默认值: 1.0",
|
||||
" 最小值: 0.1, 最大值: 10.0"
|
||||
)
|
||||
.defineInRange("loveMachineDamageFactor", 1.0, 0.1, 10.0);
|
||||
|
||||
builder.pop(); // 退出爱机配置组
|
||||
|
||||
// 可选:添加更多配置组
|
||||
builder.comment(
|
||||
"General Settings / 通用设置",
|
||||
"----------------------------------------",
|
||||
"EN: General mod settings",
|
||||
"CN: 模组通用设置"
|
||||
).push("general_settings");
|
||||
|
||||
// 这里可以添加其他通用配置
|
||||
|
||||
builder.pop(); // 退出通用配置组
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -47,6 +47,7 @@ import software.bernie.geckolib.util.GeckoLibUtil;
|
|||
import top.r3944realms.eroticdungeongame.EroticDungeon;
|
||||
import top.r3944realms.eroticdungeongame.api.event.LoveMachineTickEvent;
|
||||
import top.r3944realms.eroticdungeongame.client.util.RendererUtil;
|
||||
import top.r3944realms.eroticdungeongame.config.EDGConfig;
|
||||
import top.r3944realms.eroticdungeongame.content.EDGDamageTypes;
|
||||
import top.r3944realms.eroticdungeongame.content.block.type.LoveMachineBlock;
|
||||
import top.r3944realms.eroticdungeongame.core.register.EDGBlockEntities;
|
||||
|
|
@ -131,20 +132,20 @@ public class LoveMachineBlockEntity extends BlockEntity implements GeoBlockEntit
|
|||
ACTION.getOrDefault(loveMachineBlock.type, SpecialAction.EMPTY).doTick(pLevel, pBlockEntity, entity, pPos, pState, pLevel.getGameTime());
|
||||
if (pBlockEntity.speed < 0.05f) {
|
||||
if (pLevel.getGameTime() % 100 == 0) {
|
||||
if (pBlockEntity.hasLubricant()) {
|
||||
if (pBlockEntity.hasLubricant() || !EDGConfig.COMMON.ENABLE_LOVE_MACHINE_DAMAGE.get()) {
|
||||
if (entity instanceof LivingEntity livingEntity) {
|
||||
livingEntity.addEffect(new MobEffectInstance(MobEffects.HEAL, 20, 1, true, true, true));
|
||||
livingEntity.addEffect(new MobEffectInstance(MobEffects.DIG_SPEED, 20, 1, true, true, true));
|
||||
}
|
||||
} else entity.hurt(EDGDamageTypes.causeFuckedDamage(pLevel.registryAccess()), calcAnimSpeed(pBlockEntity.speed) * 10f);
|
||||
} else entity.hurt(EDGDamageTypes.causeFuckedDamage(pLevel.registryAccess()), (float) (calcAnimSpeed(pBlockEntity.speed) * EDGConfig.COMMON.LOVE_MACHINE_DAMAGE_FACTOR.get() * 10f));
|
||||
}
|
||||
} else {
|
||||
if (pBlockEntity.hasLubricant()) {
|
||||
if (pBlockEntity.hasLubricant() || !EDGConfig.COMMON.ENABLE_LOVE_MACHINE_DAMAGE.get()) {
|
||||
if (entity instanceof LivingEntity livingEntity) {
|
||||
livingEntity.addEffect(new MobEffectInstance(MobEffects.HEAL, 20, 1, true, true, true));
|
||||
livingEntity.addEffect(new MobEffectInstance(MobEffects.DIG_SPEED, 20, 1, true, true, true));
|
||||
}
|
||||
} else entity.hurt(EDGDamageTypes.causeFuckedDamage(pLevel.registryAccess()), calcAnimSpeed(pBlockEntity.speed));
|
||||
} else entity.hurt(EDGDamageTypes.causeFuckedDamage(pLevel.registryAccess()), (float) (calcAnimSpeed(pBlockEntity.speed) * EDGConfig.COMMON.LOVE_MACHINE_DAMAGE_FACTOR.get()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -42,12 +42,14 @@ import net.minecraftforge.common.util.FakePlayerFactory;
|
|||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import top.r3944realms.eroticdungeongame.api.workspace.Services;
|
||||
import top.r3944realms.eroticdungeongame.config.EDGConfig;
|
||||
import top.r3944realms.eroticdungeongame.content.block.ISeatBlock;
|
||||
import top.r3944realms.eroticdungeongame.content.block.blockentity.BaseSeatBlockEntity;
|
||||
import top.r3944realms.eroticdungeongame.content.entity.SeatEntity;
|
||||
import top.r3944realms.eroticdungeongame.content.item.DeviceKeyItem;
|
||||
import top.r3944realms.eroticdungeongame.content.item.FilterItem;
|
||||
import top.r3944realms.eroticdungeongame.content.util.FurnitureHelper;
|
||||
import top.r3944realms.eroticdungeongame.core.service.NorpService;
|
||||
import top.r3944realms.eroticdungeongame.core.service.SeatService;
|
||||
import top.r3944realms.lib39.core.command.ICommandHelpManager;
|
||||
import top.r3944realms.lib39.core.command.SimpleHelpCommand;
|
||||
|
|
@ -87,6 +89,7 @@ public class EDGCommand extends SimpleHelpCommand {
|
|||
private void registerCommands(@NotNull CommandDispatcher<CommandSourceStack> dispatcher) {
|
||||
// edg eroticdungeongame
|
||||
// edg help toggle <hash>
|
||||
// edg norp
|
||||
// edg device bind <pos> [player] [code] 将[player] (没有则命令执行者) 绑定在<pos>处的设备上 (如果有[code] 则顺便上锁)
|
||||
// unbind <pos> 解绑<pos>处的设备
|
||||
// [player] 解绑<player> (没有则命令执行者)
|
||||
|
|
@ -114,6 +117,10 @@ public class EDGCommand extends SimpleHelpCommand {
|
|||
// reset 重置
|
||||
getRoot()
|
||||
.executes(this::handleHelp)
|
||||
.then(
|
||||
Commands.literal("norp")
|
||||
.executes(this::edg$norp)
|
||||
)
|
||||
.then(
|
||||
Commands.literal("device")
|
||||
.requires(source -> source.hasPermission(2))
|
||||
|
|
@ -341,6 +348,9 @@ public class EDGCommand extends SimpleHelpCommand {
|
|||
|
||||
commandHelpManager.registerCommands(builder -> builder.root("edg", "commands.eroticdungeongame.root")
|
||||
.expanded(true)
|
||||
.branch("norp", "commands.help.eroticdungeongame.norp", norpBuilder -> {
|
||||
norpBuilder.leaf("norp", "commands.help.eroticdungeongame.norp.desc");
|
||||
})
|
||||
// device 命令分支
|
||||
.branch("device", "commands.help.eroticdungeongame.device", stack -> stack.hasPermission(2), deviceBuilder -> {
|
||||
deviceBuilder.expanded(false)
|
||||
|
|
@ -486,7 +496,15 @@ public class EDGCommand extends SimpleHelpCommand {
|
|||
|
||||
);
|
||||
}
|
||||
|
||||
private int edg$norp(@NotNull CommandContext<CommandSourceStack> context) throws CommandSyntaxException {
|
||||
//todo: 完善下内容
|
||||
CommandSourceStack source = context.getSource();
|
||||
ServerPlayer player = source.getPlayerOrException();
|
||||
if (EDGConfig.COMMON.ENABLE_NORP.get()) {
|
||||
boolean norp = NorpService.norp(player);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
// ============ Device Bind Methods ============
|
||||
private int edg$device$bind$pos$self(CommandContext<CommandSourceStack> context) throws CommandSyntaxException {
|
||||
BlockPos pos = BlockPosArgument.getBlockPos(context, "pos");
|
||||
|
|
|
|||
|
|
@ -50,11 +50,13 @@ public sealed abstract class AbstractPlayerDungeonData extends NBTEntitySyncData
|
|||
setPlayerBoundingBox(null);
|
||||
AABB aabb = player.getDimensions(player.getPose()).makeBoundingBox(player.position());
|
||||
player.setBoundingBox(aabb);
|
||||
setNorpTime(INVALID);
|
||||
}
|
||||
|
||||
private void setDungeonData_(@NotNull SeatEntity seat, @NotNull ISeatType type) {
|
||||
setEyeHeight(type.getEyeHeight());
|
||||
setPlayerBoundingBox(type.getPlayerBB());
|
||||
setDeviceMainBlockPos(seat.getLinkedBlockPos());
|
||||
setNorpState(IDLE);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import net.minecraft.resources.ResourceLocation;
|
|||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.intellij.lang.annotations.MagicConstant;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import top.r3944realms.eroticdungeongame.core.event.CommonHandler;
|
||||
import top.r3944realms.lib39.util.nbt.NBTReader;
|
||||
|
|
@ -35,12 +36,17 @@ public final class PlayerDungeonData extends AbstractPlayerDungeonData {
|
|||
public static final String SEAT_ANIMATION = "seat_animation";
|
||||
public static final String CURRENT_BB = "current_bb";
|
||||
public static final String CURRENT_EYE_HEIGHT = "current_eye_height";
|
||||
public static final String NORP_STATE = "norp_state";
|
||||
public static final String NORP_TIME = "norp_time";
|
||||
public static final AABB ZERO = AABB.ofSize(Vec3.ZERO, 0, 0, 0);
|
||||
public Player player;
|
||||
public BlockPos pos;
|
||||
public ResourceLocation anim;
|
||||
public AABB currentBB;
|
||||
public float currentEyeHeight;
|
||||
public long norpTime;
|
||||
@MagicConstant(intValues = {INVALID, IDLE, REQUEST})
|
||||
public int norpState;
|
||||
|
||||
public PlayerDungeonData(Player player) {
|
||||
super(CommonHandler.Game.DUNGEON_SYNC);
|
||||
|
|
@ -54,6 +60,8 @@ public final class PlayerDungeonData extends AbstractPlayerDungeonData {
|
|||
.stringIf(SEAT_ANIMATION, anim != null, () -> anim.toString())
|
||||
.compoundIf(CURRENT_BB, currentBB != null, () -> writeBB(currentBB))
|
||||
.floatValue(CURRENT_EYE_HEIGHT, currentEyeHeight, -1)
|
||||
.longValue(NORP_TIME, norpTime, -1)
|
||||
.intValue(NORP_STATE, norpState, INVALID)
|
||||
.build();
|
||||
}
|
||||
|
||||
|
|
@ -92,7 +100,9 @@ public final class PlayerDungeonData extends AbstractPlayerDungeonData {
|
|||
}
|
||||
});
|
||||
}, null)
|
||||
.floatValue(CURRENT_EYE_HEIGHT, this::setEyeHeight, -1.0f);
|
||||
.floatValue(CURRENT_EYE_HEIGHT, this::setEyeHeight, -1.0f)
|
||||
.intValue(NORP_STATE, this::setNorpState)
|
||||
.longValue(NORP_TIME, this::setNorpTime);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -144,4 +154,26 @@ public final class PlayerDungeonData extends AbstractPlayerDungeonData {
|
|||
this.currentEyeHeight = eyeHeight;
|
||||
markDirty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNorpState(int state) {
|
||||
this.norpState = state;
|
||||
markDirty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNorpState() {
|
||||
return norpState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNorpTime(long norpTime) {
|
||||
this.norpTime = norpTime;
|
||||
markDirty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getNorpTime() {
|
||||
return norpTime;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* Copyright 2025-2026 R3944Realms
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.r3944realms.eroticdungeongame.core.service;
|
||||
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import top.r3944realms.eroticdungeongame.api.capability.IPlayerDungeonData;
|
||||
import top.r3944realms.eroticdungeongame.api.workspace.Services;
|
||||
import top.r3944realms.eroticdungeongame.config.EDGConfig;
|
||||
import top.r3944realms.eroticdungeongame.core.capability.PlayerDungeonDataProvider;
|
||||
import top.r3944realms.eroticdungeongame.util.IEDGEntity;
|
||||
|
||||
public class NorpService {
|
||||
public static boolean checkCanNorp(Player player) {
|
||||
return Services.WORK_SPACE.isInDevice(player);
|
||||
}
|
||||
public static boolean norp(Player player) {
|
||||
if (checkCanNorp(player))
|
||||
return player.getCapability(PlayerDungeonDataProvider.PLAYER_DUNGEON_DATA_CAP)
|
||||
.map(data -> {
|
||||
boolean flag = false;
|
||||
switch (data.getNorpState()) {
|
||||
case IPlayerDungeonData.IDLE -> {
|
||||
flag = System.currentTimeMillis() - data.getNorpTime() >= EDGConfig.COMMON.NORP_SUCCESSFUL_COOLDOWN.get() * 1000;
|
||||
if (flag) {
|
||||
data.setNorpState(data.REQUEST);
|
||||
//todo 提醒玩家下次生效
|
||||
} else {
|
||||
// todo 还在冷却中
|
||||
}
|
||||
}
|
||||
case IPlayerDungeonData.REQUEST -> {
|
||||
flag = System.currentTimeMillis() - data.getNorpTime() >= EDGConfig.COMMON.NORP_INTERVAL_TIME.get() * 1000;
|
||||
if (flag) {
|
||||
data.setNorpState(data.IDLE);
|
||||
((IEDGEntity)player).releaseEntityFromEDGDevice(true);
|
||||
//todo 提醒玩家成功
|
||||
} else {
|
||||
// todo 提醒玩家等待间隔
|
||||
}
|
||||
}
|
||||
default -> {}
|
||||
}
|
||||
return flag;
|
||||
}).orElse(false);
|
||||
else return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,253 @@
|
|||
/*
|
||||
* Copyright 2025-2026 R3944Realms
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.r3944realms.eroticdungeongame.util;
|
||||
|
||||
import net.minecraftforge.fml.loading.FMLPaths;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
/**
|
||||
* NORP Logger - 简单的日志记录工具
|
||||
* 仅在调用时检查并写入日志,自动按天轮换
|
||||
*/
|
||||
public class NorpLogger {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger("NorpLogger");
|
||||
|
||||
// 日志文件配置
|
||||
private static final String LOG_DIR = "norp";
|
||||
private static final String LOG_FILE_PREFIX = "norp_";
|
||||
private static final String LOG_FILE_SUFFIX = ".log";
|
||||
private static final String DATE_FORMAT = "yyyy-MM-dd";
|
||||
private static final String TIMESTAMP_FORMAT = "yyyy-MM-dd HH:mm:ss";
|
||||
|
||||
// 日志级别枚举
|
||||
public enum LogLevel {
|
||||
INFO, WARNING, ERROR, DEBUG, SUCCESS
|
||||
}
|
||||
|
||||
private static final DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern(DATE_FORMAT);
|
||||
private static final DateTimeFormatter timestampFormatter = DateTimeFormatter.ofPattern(TIMESTAMP_FORMAT);
|
||||
private static final ReentrantLock lock = new ReentrantLock();
|
||||
|
||||
// 当前日志文件信息缓存
|
||||
private static Path currentLogPath = null;
|
||||
private static String currentDateStr = null;
|
||||
|
||||
/**
|
||||
* 私有构造函数 - 工具类不需要实例化
|
||||
*/
|
||||
private NorpLogger() {}
|
||||
|
||||
/**
|
||||
* 获取今天的日志文件路径
|
||||
*/
|
||||
private static Path getTodayLogPath() {
|
||||
Path mcPath = FMLPaths.GAMEDIR.get();
|
||||
String today = LocalDate.now().format(dateFormatter);
|
||||
String fileName = LOG_FILE_PREFIX + today + LOG_FILE_SUFFIX;
|
||||
return mcPath.resolve(LOG_DIR).resolve(fileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 确保日志目录存在
|
||||
*/
|
||||
private static void ensureLogDirectoryExists() {
|
||||
try {
|
||||
Path logDir = FMLPaths.GAMEDIR.get().resolve(LOG_DIR);
|
||||
if (!Files.exists(logDir)) {
|
||||
Files.createDirectories(logDir);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
LOGGER.error("Failed to create log directory", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否需要切换日志文件
|
||||
*/
|
||||
private static boolean shouldRotateLog() {
|
||||
String today = LocalDate.now().format(dateFormatter);
|
||||
return !today.equals(currentDateStr) || currentLogPath == null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前应该写入的文件路径
|
||||
*/
|
||||
private static Path getCurrentLogPath() {
|
||||
lock.lock();
|
||||
try {
|
||||
if (shouldRotateLog()) {
|
||||
currentDateStr = LocalDate.now().format(dateFormatter);
|
||||
currentLogPath = getTodayLogPath();
|
||||
ensureLogDirectoryExists();
|
||||
}
|
||||
return currentLogPath;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入日志到文件
|
||||
*/
|
||||
private static void writeToFile(String content) {
|
||||
Path logPath = getCurrentLogPath();
|
||||
|
||||
lock.lock();
|
||||
try (BufferedWriter writer = new BufferedWriter(
|
||||
new OutputStreamWriter(
|
||||
new FileOutputStream(logPath.toFile(), true),
|
||||
StandardCharsets.UTF_8
|
||||
)
|
||||
)) {
|
||||
writer.write(content);
|
||||
writer.newLine();
|
||||
writer.flush();
|
||||
} catch (IOException e) {
|
||||
LOGGER.error("Failed to write to log file: {}", logPath, e);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化日志消息
|
||||
*/
|
||||
private static String formatMessage(String level, String message, Object... args) {
|
||||
String formattedMsg = args.length > 0 ? String.format(message, args) : message;
|
||||
return String.format("%s [%s] %s",
|
||||
LocalDateTime.now().format(timestampFormatter),
|
||||
level,
|
||||
formattedMsg);
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录日志(核心方法)
|
||||
*/
|
||||
public static void log(String level, String message, Object... args) {
|
||||
String formattedMessage = formatMessage(level, message, args);
|
||||
writeToFile(formattedMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用LogLevel枚举记录日志
|
||||
*/
|
||||
public static void log(LogLevel level, String message, Object... args) {
|
||||
log(level.name(), message, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录INFO级别日志
|
||||
*/
|
||||
public static void info(String message, Object... args) {
|
||||
log(LogLevel.INFO, message, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录WARNING级别日志
|
||||
*/
|
||||
public static void warning(String message, Object... args) {
|
||||
log(LogLevel.WARNING, message, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录ERROR级别日志
|
||||
*/
|
||||
public static void error(String message, Object... args) {
|
||||
log(LogLevel.ERROR, message, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录DEBUG级别日志
|
||||
*/
|
||||
public static void debug(String message, Object... args) {
|
||||
log(LogLevel.DEBUG, message, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录SUCCESS级别日志
|
||||
*/
|
||||
public static void success(String message, Object... args) {
|
||||
log(LogLevel.SUCCESS, message, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录NORP事件
|
||||
*/
|
||||
public static void logNorpEvent(String playerName, String action, String result) {
|
||||
info("NORP Event - Player: %s, Action: %s, Result: %s", playerName, action, result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录NORP成功
|
||||
*/
|
||||
public static void logNorpSuccess(String playerName, String action) {
|
||||
success("NORP Success - Player: %s, Action: %s", playerName, action);
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录NORP失败
|
||||
*/
|
||||
public static void logNorpFailure(String playerName, String action, String reason) {
|
||||
warning("NORP Failure - Player: %s, Action: %s, Reason: %s", playerName, action, reason);
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录带标题的分隔线
|
||||
*/
|
||||
public static void logSeparator(String title) {
|
||||
String separator = "════════════════════════════════════════════";
|
||||
info("%s %s %s", separator, title, separator);
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录空行
|
||||
*/
|
||||
public static void logEmptyLine() {
|
||||
writeToFile("");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前日志文件路径
|
||||
*/
|
||||
public static Path getCurrentLogFilePath() {
|
||||
return getCurrentLogPath();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查指定日期的日志文件是否存在
|
||||
*/
|
||||
public static boolean logExistsForDate(LocalDate date) {
|
||||
String fileName = LOG_FILE_PREFIX + date.format(dateFormatter) + LOG_FILE_SUFFIX;
|
||||
Path logPath = FMLPaths.GAMEDIR.get().resolve(LOG_DIR).resolve(fileName);
|
||||
return Files.exists(logPath);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user