aaaaaaaaaaa
This commit is contained in:
parent
8cc8f6a427
commit
5d3f71cef5
@ -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.034
|
||||
mod_version=1.14.514.037
|
||||
maven_group=semmiedev
|
||||
archives_base_name=disc_jockey_revive
|
||||
# Dependencies
|
||||
|
25
src/main/java/semmiedev/disc_jockey_revive/DebugLogger.java
Normal file
25
src/main/java/semmiedev/disc_jockey_revive/DebugLogger.java
Normal file
@ -0,0 +1,25 @@
|
||||
package semmiedev.disc_jockey_revive;
|
||||
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
// 这玩意一般不用,我自己用的,但是不想删
|
||||
public class DebugLogger {
|
||||
|
||||
public static void log(String message) {
|
||||
if (Main.config != null && Main.config.debugModeEnabled) {
|
||||
Main.LOGGER.info("[DiscJockeyRevive-调试] " + message);
|
||||
}
|
||||
}
|
||||
|
||||
public static void log(String format, Object... arguments) {
|
||||
if (Main.config != null && Main.config.debugModeEnabled) {
|
||||
Main.LOGGER.info("[DiscJockeyRevive-调试] " + format, arguments);
|
||||
}
|
||||
}
|
||||
|
||||
public static void log(String message, Throwable t) {
|
||||
if (Main.config != null && Main.config.debugModeEnabled) {
|
||||
Main.LOGGER.error("[DiscJockeyRevive-调试] " + message, t);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,172 @@
|
||||
package semmiedev.disc_jockey_revive;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
import net.minecraft.client.util.InputUtil;
|
||||
import net.minecraft.block.enums.NoteBlockInstrument;
|
||||
import net.minecraft.text.Text;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class KeyMappingManager {
|
||||
private static final File MAPPINGS_FILE = new File(FabricLoader.getInstance().getConfigDir().toFile(), "disc_jockey" + "/key_mappings.json");
|
||||
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
|
||||
private static final Type MAPPING_TYPE = new TypeToken<HashMap<String, NoteData>>() {}.getType();
|
||||
|
||||
private Map<InputUtil.Key, Note> mappings = new HashMap<>();
|
||||
|
||||
private static class NoteData {
|
||||
String instrument;
|
||||
byte note;
|
||||
|
||||
public NoteData(Note note) {
|
||||
this.instrument = note.instrument().name();
|
||||
this.note = note.note();
|
||||
}
|
||||
|
||||
public Note toNote() {
|
||||
try {
|
||||
NoteBlockInstrument instrumentEnum = NoteBlockInstrument.valueOf(instrument);
|
||||
return new Note(instrumentEnum, note);
|
||||
} catch (IllegalArgumentException e) {
|
||||
Main.LOGGER.error("键位映射中出现未知乐器 '{}' ", instrument);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public KeyMappingManager() {
|
||||
loadMappings();
|
||||
}
|
||||
|
||||
public void loadMappings() {
|
||||
if (!MAPPINGS_FILE.exists()) {
|
||||
Main.LOGGER.info("未找到键映射文件,正在创建默认的类似于 FL Studio 的映射。");
|
||||
mappings.clear();
|
||||
|
||||
NoteBlockInstrument dirt = NoteBlockInstrument.HARP;
|
||||
NoteBlockInstrument wood = NoteBlockInstrument.BASS;
|
||||
// NoteBlockInstrument gold = NoteBlockInstrument.BELL;
|
||||
|
||||
mappings.put(InputUtil.fromKeyCode(InputUtil.GLFW_KEY_Z, 0), new Note(wood, (byte) 6));
|
||||
mappings.put(InputUtil.fromKeyCode(InputUtil.GLFW_KEY_S, 0), new Note(wood, (byte) 7));
|
||||
mappings.put(InputUtil.fromKeyCode(InputUtil.GLFW_KEY_X, 0), new Note(wood, (byte) 8));
|
||||
mappings.put(InputUtil.fromKeyCode(InputUtil.GLFW_KEY_D, 0), new Note(wood, (byte) 9));
|
||||
mappings.put(InputUtil.fromKeyCode(InputUtil.GLFW_KEY_C, 0), new Note(wood, (byte) 10));
|
||||
mappings.put(InputUtil.fromKeyCode(InputUtil.GLFW_KEY_V, 0), new Note(wood, (byte) 11));
|
||||
|
||||
mappings.put(InputUtil.fromKeyCode(InputUtil.GLFW_KEY_G, 0), new Note(dirt, (byte) 0));
|
||||
mappings.put(InputUtil.fromKeyCode(InputUtil.GLFW_KEY_B, 0), new Note(dirt, (byte) 1));
|
||||
mappings.put(InputUtil.fromKeyCode(InputUtil.GLFW_KEY_H, 0), new Note(dirt, (byte) 2));
|
||||
mappings.put(InputUtil.fromKeyCode(InputUtil.GLFW_KEY_N, 0), new Note(dirt, (byte) 3));
|
||||
mappings.put(InputUtil.fromKeyCode(InputUtil.GLFW_KEY_J, 0), new Note(dirt, (byte) 4));
|
||||
mappings.put(InputUtil.fromKeyCode(InputUtil.GLFW_KEY_M, 0), new Note(dirt, (byte) 5));
|
||||
mappings.put(InputUtil.fromKeyCode(InputUtil.GLFW_KEY_COMMA, 0), new Note(dirt, (byte) 6));
|
||||
|
||||
mappings.put(InputUtil.fromKeyCode(InputUtil.GLFW_KEY_Q, 0), new Note(dirt, (byte) 6));
|
||||
mappings.put(InputUtil.fromKeyCode(InputUtil.GLFW_KEY_2, 0), new Note(dirt, (byte) 7));
|
||||
mappings.put(InputUtil.fromKeyCode(InputUtil.GLFW_KEY_W, 0), new Note(dirt, (byte) 8));
|
||||
mappings.put(InputUtil.fromKeyCode(InputUtil.GLFW_KEY_3, 0), new Note(dirt, (byte) 9));
|
||||
mappings.put(InputUtil.fromKeyCode(InputUtil.GLFW_KEY_E, 0), new Note(dirt, (byte) 10));
|
||||
mappings.put(InputUtil.fromKeyCode(InputUtil.GLFW_KEY_R, 0), new Note(dirt, (byte) 11));
|
||||
mappings.put(InputUtil.fromKeyCode(InputUtil.GLFW_KEY_5, 0), new Note(dirt, (byte) 12));
|
||||
mappings.put(InputUtil.fromKeyCode(InputUtil.GLFW_KEY_T, 0), new Note(dirt, (byte) 13));
|
||||
mappings.put(InputUtil.fromKeyCode(InputUtil.GLFW_KEY_6, 0), new Note(dirt, (byte) 14));
|
||||
mappings.put(InputUtil.fromKeyCode(InputUtil.GLFW_KEY_Y, 0), new Note(dirt, (byte) 15));
|
||||
mappings.put(InputUtil.fromKeyCode(InputUtil.GLFW_KEY_7, 0), new Note(dirt, (byte) 16));
|
||||
mappings.put(InputUtil.fromKeyCode(InputUtil.GLFW_KEY_U, 0), new Note(dirt, (byte) 17));
|
||||
mappings.put(InputUtil.fromKeyCode(InputUtil.GLFW_KEY_I, 0), new Note(dirt, (byte) 18));
|
||||
mappings.put(InputUtil.fromKeyCode(InputUtil.GLFW_KEY_9, 0), new Note(dirt, (byte) 19));
|
||||
mappings.put(InputUtil.fromKeyCode(InputUtil.GLFW_KEY_O, 0), new Note(dirt, (byte) 20));
|
||||
mappings.put(InputUtil.fromKeyCode(InputUtil.GLFW_KEY_0, 0), new Note(dirt, (byte) 21));
|
||||
mappings.put(InputUtil.fromKeyCode(InputUtil.GLFW_KEY_P, 0), new Note(dirt, (byte) 22));
|
||||
mappings.put(InputUtil.fromKeyCode(InputUtil.GLFW_KEY_LEFT_BRACKET, 0), new Note(dirt, (byte) 23));
|
||||
mappings.put(InputUtil.fromKeyCode(InputUtil.GLFW_KEY_EQUAL, 0), new Note(dirt, (byte) 24));
|
||||
|
||||
|
||||
saveMappings();
|
||||
return;
|
||||
}
|
||||
|
||||
try (FileReader reader = new FileReader(MAPPINGS_FILE)) {
|
||||
HashMap<String, NoteData> loadedData = GSON.fromJson(reader, MAPPING_TYPE);
|
||||
mappings.clear();
|
||||
if (loadedData != null) {
|
||||
for (Map.Entry<String, NoteData> entry : loadedData.entrySet()) {
|
||||
InputUtil.Key key = InputUtil.fromTranslationKey(entry.getKey());
|
||||
Note note = entry.getValue().toNote();
|
||||
if (key != InputUtil.UNKNOWN_KEY && note != null) {
|
||||
mappings.put(key, note);
|
||||
}
|
||||
}
|
||||
}
|
||||
Main.LOGGER.info("已加载按键映射。");
|
||||
} catch (IOException e) {
|
||||
Main.LOGGER.error("加载按键映射失败。", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void saveMappings() {
|
||||
MAPPINGS_FILE.getParentFile().mkdirs();
|
||||
try (FileWriter writer = new FileWriter(MAPPINGS_FILE)) {
|
||||
HashMap<String, NoteData> dataToSave = new HashMap<>();
|
||||
for (Map.Entry<InputUtil.Key, Note> entry : mappings.entrySet()) {
|
||||
dataToSave.put(entry.getKey().getTranslationKey(), new NoteData(entry.getValue()));
|
||||
}
|
||||
GSON.toJson(dataToSave, writer);
|
||||
Main.LOGGER.info("已保存按键映射。");
|
||||
} catch (IOException e) {
|
||||
Main.LOGGER.error("保存按键映射失败。", e);
|
||||
}
|
||||
}
|
||||
|
||||
public Note getNoteForKey(InputUtil.Key key) {
|
||||
return mappings.get(key);
|
||||
}
|
||||
|
||||
public void setMapping(InputUtil.Key key, Note note) {
|
||||
mappings.put(key, note);
|
||||
}
|
||||
|
||||
public void removeMapping(InputUtil.Key key) {
|
||||
mappings.remove(key);
|
||||
}
|
||||
|
||||
public Map<InputUtil.Key, Note> getMappings() {
|
||||
return new HashMap<>(mappings);
|
||||
}
|
||||
|
||||
public static String getNoteDisplayName(Note note) {
|
||||
if (note == null) return "未设置";
|
||||
|
||||
String instrumentTranslationKey = "block.minecraft.note_block.instrument." + note.instrument().asString();
|
||||
String instrumentName = Text.translatable(instrumentTranslationKey).getString();
|
||||
|
||||
int pitch = note.note();
|
||||
String[] noteNames = {"F#", "G", "G#", "A", "A#", "B", "C", "C#", "D", "D#", "E", "F"};
|
||||
|
||||
int octave;
|
||||
if (pitch >= 19) {
|
||||
octave = 1;
|
||||
} else if (pitch >= 7) {
|
||||
octave = 0;
|
||||
} else {
|
||||
octave = -1;
|
||||
}
|
||||
|
||||
int noteIndexInArray = (pitch - 7 + 12 * 2) % 12;
|
||||
String noteName = noteNames[noteIndexInArray];
|
||||
|
||||
return instrumentName + " " + noteName + "(" + octave + ")";
|
||||
}
|
||||
}
|
475
src/main/java/semmiedev/disc_jockey_revive/LiveDjPlayer.java
Normal file
475
src/main/java/semmiedev/disc_jockey_revive/LiveDjPlayer.java
Normal file
@ -0,0 +1,475 @@
|
||||
package semmiedev.disc_jockey_revive;
|
||||
|
||||
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.Blocks;
|
||||
import net.minecraft.block.enums.NoteBlockInstrument;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.gui.hud.ChatHud;
|
||||
import net.minecraft.client.network.ClientPlayerEntity;
|
||||
import net.minecraft.client.network.PlayerListEntry;
|
||||
import net.minecraft.client.world.ClientWorld;
|
||||
import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket;
|
||||
import net.minecraft.network.packet.c2s.play.PlayerMoveC2SPacket;
|
||||
import net.minecraft.state.property.Properties;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.Formatting;
|
||||
import net.minecraft.util.Hand;
|
||||
import net.minecraft.util.Pair;
|
||||
import net.minecraft.util.hit.BlockHitResult;
|
||||
import net.minecraft.util.math.*;
|
||||
import net.minecraft.world.GameMode;
|
||||
import org.apache.commons.lang3.NotImplementedException;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class LiveDjPlayer implements ClientTickEvents.StartWorldTick {
|
||||
|
||||
private HashMap<NoteBlockInstrument, HashMap<Byte, BlockPos>> noteBlocks = null;
|
||||
private boolean tuned = false;
|
||||
private boolean tuningRequested = false;
|
||||
|
||||
private long last100MsSpanAt = -1L;
|
||||
private int last100MsSpanEstimatedPackets = 0;
|
||||
final private int last100MsReducePacketsAfter = 300 / 10, last100MsStopPacketsAfter = 450 / 10;
|
||||
private long reducePacketsUntil = -1L, stopPacketsUntil = -1L;
|
||||
private long lastLookSentAt = -1L, lastSwingSentAt = -1L;
|
||||
private long lastInteractAt = -1;
|
||||
private float availableInteracts = 8;
|
||||
private int tuneInitialUntunedBlocks = -1;
|
||||
private HashMap<BlockPos, Pair<Integer, Long>> notePredictions = new HashMap<>();
|
||||
|
||||
public HashMap<Block, Integer> missingInstrumentBlocks = new HashMap<>();
|
||||
|
||||
public LiveDjPlayer() {
|
||||
Main.TICK_LISTENERS.add(this);
|
||||
}
|
||||
|
||||
// 调音
|
||||
public synchronized void startTuning() {
|
||||
if (tuned && noteBlocks != null) {
|
||||
MinecraftClient.getInstance().inGameHud.getChatHud().addMessage(Text.translatable(Main.MOD_ID+".player.retuning").formatted(Formatting.YELLOW));
|
||||
} else {
|
||||
MinecraftClient.getInstance().inGameHud.getChatHud().addMessage(Text.translatable(Main.MOD_ID+".player.tuning_started").formatted(Formatting.YELLOW));
|
||||
}
|
||||
noteBlocks = null;
|
||||
notePredictions.clear();
|
||||
tuned = false;
|
||||
tuneInitialUntunedBlocks = -1;
|
||||
tuningRequested = true;
|
||||
|
||||
last100MsSpanAt = System.currentTimeMillis();
|
||||
last100MsSpanEstimatedPackets = 0;
|
||||
reducePacketsUntil = -1L;
|
||||
stopPacketsUntil = -1L;
|
||||
lastLookSentAt = -1L;
|
||||
lastSwingSentAt = -1L;
|
||||
lastInteractAt = -1;
|
||||
availableInteracts = 8;
|
||||
}
|
||||
|
||||
// 停止调音
|
||||
public synchronized void stopTuning() {
|
||||
tuningRequested = false;
|
||||
}
|
||||
|
||||
// 是否正在调音
|
||||
public boolean isTuningActive() {
|
||||
return tuningRequested;
|
||||
}
|
||||
|
||||
// 是否已调音
|
||||
public boolean isTuned() {
|
||||
return tuned;
|
||||
}
|
||||
|
||||
public HashMap<NoteBlockInstrument, HashMap<Byte, BlockPos>> getNoteBlocks() {
|
||||
return noteBlocks;
|
||||
}
|
||||
|
||||
// 播放
|
||||
public boolean playNoteBlock(Note note) {
|
||||
MinecraftClient client = MinecraftClient.getInstance();
|
||||
ClientWorld world = client.world;
|
||||
ClientPlayerEntity player = client.player;
|
||||
GameMode gameMode = client.interactionManager == null ? null : client.interactionManager.getCurrentGameMode();
|
||||
|
||||
if (world == null || player == null || gameMode == null || !gameMode.isSurvivalLike()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (noteBlocks == null || !tuned) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nullable BlockPos blockPos = noteBlocks.get(note.instrument()).get(note.note());
|
||||
if(blockPos == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!canInteractWith(player, blockPos)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return sendNotePacket(blockPos);
|
||||
}
|
||||
|
||||
// 获取可交互的
|
||||
private boolean sendNotePacket(BlockPos blockPos) {
|
||||
MinecraftClient client = MinecraftClient.getInstance();
|
||||
ClientPlayerEntity player = client.player;
|
||||
long now = System.currentTimeMillis();
|
||||
|
||||
if(last100MsSpanAt != -1L && now - last100MsSpanAt >= 100) {
|
||||
last100MsSpanEstimatedPackets = 0;
|
||||
last100MsSpanAt = now;
|
||||
}else if (last100MsSpanAt == -1L) {
|
||||
last100MsSpanAt = now;
|
||||
last100MsSpanEstimatedPackets = 0;
|
||||
}
|
||||
|
||||
if (stopPacketsUntil != -1L && stopPacketsUntil >= now) {
|
||||
return false;
|
||||
}
|
||||
if (reducePacketsUntil != -1L && reducePacketsUntil >= now && last100MsSpanEstimatedPackets >= last100MsReducePacketsAfter) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(lastInteractAt != -1L) {
|
||||
availableInteracts += ((System.currentTimeMillis() - lastInteractAt) / (310.0f / 8.0f));
|
||||
availableInteracts = Math.min(8f, Math.max(0f, availableInteracts));
|
||||
}else {
|
||||
availableInteracts = 8f;
|
||||
lastInteractAt = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
if (availableInteracts < 1f) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Vec3d unit = Vec3d.ofCenter(blockPos, 0.5).subtract(player.getEyePos()).normalize();
|
||||
boolean packetsSent = false;
|
||||
|
||||
if((lastLookSentAt == -1L || now - lastLookSentAt >= 50) && last100MsSpanEstimatedPackets < last100MsReducePacketsAfter) {
|
||||
client.getNetworkHandler().sendPacket(new PlayerMoveC2SPacket.LookAndOnGround(MathHelper.wrapDegrees((float) (MathHelper.atan2(unit.z, unit.x) * 57.2957763671875) - 90.0f), MathHelper.wrapDegrees((float) (-(MathHelper.atan2(unit.y, Math.sqrt(unit.x * unit.x + unit.z * unit.z)) * 57.2957763671875))), true, false));
|
||||
last100MsSpanEstimatedPackets++;
|
||||
lastLookSentAt = now;
|
||||
packetsSent = true;
|
||||
} else if (last100MsSpanEstimatedPackets >= last100MsReducePacketsAfter) {
|
||||
reducePacketsUntil = Math.max(reducePacketsUntil, now + 500);
|
||||
}
|
||||
|
||||
if(last100MsSpanEstimatedPackets < last100MsStopPacketsAfter) {
|
||||
client.player.networkHandler.sendPacket(new PlayerActionC2SPacket(PlayerActionC2SPacket.Action.START_DESTROY_BLOCK, blockPos, Direction.UP, 0));
|
||||
last100MsSpanEstimatedPackets++;
|
||||
client.player.networkHandler.sendPacket(new PlayerActionC2SPacket(PlayerActionC2SPacket.Action.ABORT_DESTROY_BLOCK, blockPos, Direction.UP, 0));
|
||||
last100MsSpanEstimatedPackets++;
|
||||
lastInteractAt = now;
|
||||
availableInteracts -= 1f;
|
||||
packetsSent = true;
|
||||
} else {
|
||||
Main.LOGGER.info("LiveDjPlayer: 短暂暂停所有数据包,因为速率受限!");
|
||||
stopPacketsUntil = Math.max(stopPacketsUntil, now + 250);
|
||||
reducePacketsUntil = Math.max(reducePacketsUntil, now + 10000);
|
||||
}
|
||||
|
||||
if((lastSwingSentAt == -1L || now - lastSwingSentAt >= 50) && last100MsSpanEstimatedPackets < last100MsReducePacketsAfter) {
|
||||
client.executeSync(() -> client.player.swingHand(Hand.MAIN_HAND));
|
||||
lastSwingSentAt = now;
|
||||
last100MsSpanEstimatedPackets++;
|
||||
packetsSent = true;
|
||||
} else if (last100MsSpanEstimatedPackets >= last100MsReducePacketsAfter) {
|
||||
reducePacketsUntil = Math.max(reducePacketsUntil, now + 500);
|
||||
}
|
||||
|
||||
return packetsSent;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onStartTick(ClientWorld world) {
|
||||
MinecraftClient client = MinecraftClient.getInstance();
|
||||
if(world == null || client.world == null || client.player == null) return;
|
||||
|
||||
long now = System.currentTimeMillis();
|
||||
if(last100MsSpanAt != -1L && now - last100MsSpanAt >= 100) {
|
||||
last100MsSpanEstimatedPackets = 0;
|
||||
last100MsSpanAt = now;
|
||||
}else if (last100MsSpanAt == -1L) {
|
||||
last100MsSpanAt = now;
|
||||
last100MsSpanEstimatedPackets = 0;
|
||||
}
|
||||
if(lastInteractAt != -1L) {
|
||||
availableInteracts += ((System.currentTimeMillis() - lastInteractAt) / (310.0f / 8.0f));
|
||||
availableInteracts = Math.min(8f, Math.max(0f, availableInteracts));
|
||||
}else {
|
||||
availableInteracts = 8f;
|
||||
lastInteractAt = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
ArrayList<BlockPos> outdatedPredictions = new ArrayList<>();
|
||||
for(Map.Entry<BlockPos, Pair<Integer, Long>> entry : notePredictions.entrySet()) {
|
||||
if(entry.getValue().getRight() < System.currentTimeMillis())
|
||||
outdatedPredictions.add(entry.getKey());
|
||||
}
|
||||
for(BlockPos outdatedPrediction : outdatedPredictions) notePredictions.remove(outdatedPrediction);
|
||||
|
||||
if (tuningRequested && !tuned) {
|
||||
ClientPlayerEntity player = client.getInstance().player;
|
||||
GameMode gameMode = client.interactionManager == null ? null : client.interactionManager.getCurrentGameMode();
|
||||
if (player == null || gameMode == null || !gameMode.isSurvivalLike()) {
|
||||
noteBlocks = null;
|
||||
notePredictions.clear();
|
||||
tuned = false;
|
||||
tuneInitialUntunedBlocks = -1;
|
||||
tuningRequested = false;
|
||||
client.inGameHud.getChatHud().addMessage(Text.translatable(Main.MOD_ID+".player.invalid_state_tuning").formatted(Formatting.RED));
|
||||
return;
|
||||
}
|
||||
|
||||
if (noteBlocks == null) {
|
||||
noteBlocks = new HashMap<>();
|
||||
|
||||
HashMap<NoteBlockInstrument, ArrayList<BlockPos>> noteblocksForInstrument = new HashMap<>();
|
||||
for(NoteBlockInstrument instrument : NoteBlockInstrument.values())
|
||||
noteblocksForInstrument.put(instrument, new ArrayList<>());
|
||||
final Vec3d playerEyePos = player.getEyePos();
|
||||
|
||||
final int maxOffset;
|
||||
if(Main.config.expectedServerVersion == ModConfig.ExpectedServerVersion.v1_20_4_Or_Earlier) {
|
||||
maxOffset = 7;
|
||||
}else if(Main.config.expectedServerVersion == ModConfig.ExpectedServerVersion.v1_20_5_Or_Later) {
|
||||
maxOffset = (int) Math.ceil(player.getBlockInteractionRange() + 1.0 + 1.0);
|
||||
}else if(Main.config.expectedServerVersion == ModConfig.ExpectedServerVersion.All) {
|
||||
maxOffset = Math.min(7, (int) Math.ceil(player.getBlockInteractionRange() + 1.0 + 1.0));
|
||||
}else {
|
||||
throw new NotImplementedException("ExpectedServerVersion Value not implemented: " + Main.config.expectedServerVersion.name());
|
||||
}
|
||||
final ArrayList<Integer> orderedOffsets = new ArrayList<>();
|
||||
for(int offset = 0; offset <= maxOffset; offset++) {
|
||||
orderedOffsets.add(offset);
|
||||
if(offset != 0) orderedOffsets.add(offset * -1);
|
||||
}
|
||||
|
||||
for(NoteBlockInstrument instrument : noteblocksForInstrument.keySet().toArray(new NoteBlockInstrument[0])) {
|
||||
for (int y : orderedOffsets) {
|
||||
for (int x : orderedOffsets) {
|
||||
for (int z : orderedOffsets) {
|
||||
Vec3d vec3d = playerEyePos.add(x, y, z);
|
||||
BlockPos blockPos = new BlockPos(MathHelper.floor(vec3d.x), MathHelper.floor(vec3d.y), MathHelper.floor(vec3d.z));
|
||||
if (!canInteractWith(player, blockPos))
|
||||
continue;
|
||||
BlockState blockState = world.getBlockState(blockPos);
|
||||
if (!blockState.isOf(Blocks.NOTE_BLOCK) || !world.isAir(blockPos.up()))
|
||||
continue;
|
||||
|
||||
if (blockState.get(Properties.INSTRUMENT) == instrument)
|
||||
noteblocksForInstrument.get(instrument).add(blockPos);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ArrayList<Note> neededNotes = new ArrayList<>();
|
||||
|
||||
if (Main.keyMappingManager != null) {
|
||||
for (Note mappedNote : Main.keyMappingManager.getMappings().values()) {
|
||||
if (mappedNote != null && !neededNotes.contains(mappedNote)) {
|
||||
neededNotes.add(mappedNote);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ArrayList<Note> capturedNotes = new ArrayList<>();
|
||||
for(Note note : neededNotes) {
|
||||
ArrayList<BlockPos> availableBlocks = noteblocksForInstrument.get(note.instrument());
|
||||
if(availableBlocks == null) {
|
||||
getNotesMapForInstrument(note.instrument()).put(note.note(), null);
|
||||
continue;
|
||||
}
|
||||
BlockPos bestBlockPos = null;
|
||||
int bestBlockTuningSteps = Integer.MAX_VALUE;
|
||||
for(BlockPos blockPos : availableBlocks) {
|
||||
boolean alreadyAssigned = false;
|
||||
if (noteBlocks != null) {
|
||||
for (HashMap<Byte, BlockPos> instrumentNotes : noteBlocks.values()) {
|
||||
if (instrumentNotes != null && instrumentNotes.containsValue(blockPos)) {
|
||||
alreadyAssigned = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (alreadyAssigned) continue;
|
||||
|
||||
int wantedNote = note.note();
|
||||
BlockState blockState = world.getBlockState(blockPos);
|
||||
if (!blockState.contains(Properties.NOTE)) continue;
|
||||
int currentNote = blockState.get(Properties.NOTE);
|
||||
int tuningSteps = wantedNote >= currentNote ? wantedNote - currentNote : (25 - currentNote) + wantedNote;
|
||||
|
||||
if(tuningSteps < bestBlockTuningSteps) {
|
||||
bestBlockPos = blockPos;
|
||||
bestBlockTuningSteps = tuningSteps;
|
||||
}
|
||||
}
|
||||
|
||||
if(bestBlockPos != null) {
|
||||
capturedNotes.add(note);
|
||||
availableBlocks.remove(bestBlockPos);
|
||||
getNotesMapForInstrument(note.instrument()).put(note.note(), bestBlockPos);
|
||||
}
|
||||
}
|
||||
|
||||
ArrayList<Note> missingNotes = new ArrayList<>(neededNotes);
|
||||
missingNotes.removeAll(capturedNotes);
|
||||
if (!missingNotes.isEmpty()) {
|
||||
ChatHud chatHud = MinecraftClient.getInstance().inGameHud.getChatHud();
|
||||
chatHud.addMessage(Text.translatable(Main.MOD_ID+".player.invalid_note_blocks").formatted(Formatting.RED));
|
||||
|
||||
HashMap<Block, Integer> missing = new HashMap<>();
|
||||
for (Note note : missingNotes) {
|
||||
Block block = Note.INSTRUMENT_BLOCKS.get(note.instrument());
|
||||
Integer got = missing.get(block);
|
||||
if (got == null) got = 0;
|
||||
missing.put(block, got + 1);
|
||||
}
|
||||
missingInstrumentBlocks = missing;
|
||||
missing.forEach((block, integer) -> chatHud.addMessage(Text.literal(block.getName().getString()+" × "+integer).formatted(Formatting.RED)));
|
||||
} else {
|
||||
missingInstrumentBlocks.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int ping = 0;
|
||||
{
|
||||
PlayerListEntry playerListEntry;
|
||||
if (client.getNetworkHandler() != null && (playerListEntry = client.getNetworkHandler().getPlayerListEntry(client.player.getGameProfile().getId())) != null)
|
||||
ping = playerListEntry.getLatency();
|
||||
}
|
||||
|
||||
int fullyTunedBlocks = 0;
|
||||
HashMap<BlockPos, Integer> untunedNotes = new HashMap<>();
|
||||
|
||||
if (noteBlocks != null) {
|
||||
for (HashMap<Byte, BlockPos> instrumentNotes : noteBlocks.values()) {
|
||||
if (instrumentNotes == null) continue;
|
||||
for (Map.Entry<Byte, BlockPos> entry : instrumentNotes.entrySet()) {
|
||||
BlockPos blockPos = entry.getValue();
|
||||
Byte wantedNote = entry.getKey();
|
||||
|
||||
if (blockPos == null) continue;
|
||||
|
||||
BlockState blockState = world.getBlockState(blockPos);
|
||||
if (!blockState.contains(Properties.NOTE)) {
|
||||
Main.LOGGER.warn("LiveDjPlayer: 音符盒在 {} 处在调音过程中改变了状态", blockPos);
|
||||
noteBlocks = null;
|
||||
tuned = false;
|
||||
tuningRequested = false;
|
||||
return;
|
||||
}
|
||||
|
||||
int assumedNote = notePredictions.containsKey(blockPos) ? notePredictions.get(blockPos).getLeft() : blockState.get(Properties.NOTE);
|
||||
|
||||
byte wantedNotePrimitive = wantedNote.byteValue();
|
||||
|
||||
if(assumedNote == wantedNotePrimitive && blockState.get(Properties.NOTE).intValue() == wantedNotePrimitive) {
|
||||
fullyTunedBlocks++;
|
||||
} else if (assumedNote != wantedNotePrimitive) {
|
||||
untunedNotes.put(blockPos, blockState.get(Properties.NOTE).intValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int existingUniqueNotesCount = 0;
|
||||
if (noteBlocks != null) {
|
||||
for(HashMap<Byte, BlockPos> instrumentNotes : noteBlocks.values()) {
|
||||
if (instrumentNotes != null) {
|
||||
for (BlockPos pos : instrumentNotes.values()) {
|
||||
if (pos != null) {
|
||||
existingUniqueNotesCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(untunedNotes.isEmpty() && fullyTunedBlocks == existingUniqueNotesCount) {
|
||||
if(lastInteractAt == -1 || System.currentTimeMillis() - lastInteractAt >= ping * 2 + 100) {
|
||||
tuned = true;
|
||||
tuningRequested = false;
|
||||
tuneInitialUntunedBlocks = -1;
|
||||
client.inGameHud.getChatHud().addMessage(Text.translatable(Main.MOD_ID+".player.tuned").formatted(Formatting.GREEN));
|
||||
}
|
||||
} else {
|
||||
tuned = false;
|
||||
if(tuneInitialUntunedBlocks == -1 || tuneInitialUntunedBlocks < untunedNotes.size())
|
||||
tuneInitialUntunedBlocks = untunedNotes.size();
|
||||
if (availableInteracts >= 1f && !untunedNotes.isEmpty()) {
|
||||
BlockPos blockPosToTune = untunedNotes.keySet().iterator().next();
|
||||
int currentNote = untunedNotes.get(blockPosToTune);
|
||||
Byte targetNote = null;
|
||||
if (noteBlocks != null) {
|
||||
for (HashMap<Byte, BlockPos> instrumentNotes : noteBlocks.values()) {
|
||||
if (instrumentNotes != null) {
|
||||
for (Map.Entry<Byte, BlockPos> entry : instrumentNotes.entrySet()) {
|
||||
if (blockPosToTune.equals(entry.getValue())) {
|
||||
targetNote = entry.getKey();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (targetNote != null) break;
|
||||
}
|
||||
}
|
||||
|
||||
if (targetNote != null) {
|
||||
byte targetNotePrimitive = targetNote.byteValue();
|
||||
int tuningSteps = targetNotePrimitive >= currentNote ? targetNotePrimitive - currentNote : (25 - currentNote) + targetNotePrimitive;
|
||||
|
||||
if (tuningSteps > 0) {
|
||||
int predictedNote = (currentNote + 1) % 25;
|
||||
notePredictions.put(blockPosToTune, new Pair<>(predictedNote, System.currentTimeMillis() + ping * 2 + 100));
|
||||
client.interactionManager.interactBlock(client.player, Hand.MAIN_HAND, new BlockHitResult(Vec3d.of(blockPosToTune), Direction.UP, blockPosToTune, false));
|
||||
lastInteractAt = System.currentTimeMillis();
|
||||
availableInteracts -= 1f;
|
||||
client.player.swingHand(Hand.MAIN_HAND);
|
||||
} else {
|
||||
untunedNotes.remove(blockPosToTune);
|
||||
}
|
||||
} else {
|
||||
untunedNotes.remove(blockPosToTune);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private HashMap<Byte, BlockPos> getNotesMapForInstrument(NoteBlockInstrument instrument) {
|
||||
if (noteBlocks == null) {
|
||||
noteBlocks = new HashMap<>();
|
||||
}
|
||||
return noteBlocks.computeIfAbsent(instrument, k -> new HashMap<>());
|
||||
}
|
||||
|
||||
// 检测玩家是否可以与指定方块进行交互
|
||||
public boolean canInteractWith(ClientPlayerEntity player, BlockPos blockPos) {
|
||||
final Vec3d eyePos = player.getEyePos();
|
||||
if(Main.config.expectedServerVersion == ModConfig.ExpectedServerVersion.v1_20_4_Or_Earlier) {
|
||||
return eyePos.squaredDistanceTo(blockPos.toCenterPos()) <= 6.0 * 6.0;
|
||||
}else if(Main.config.expectedServerVersion == ModConfig.ExpectedServerVersion.v1_20_5_Or_Later) {
|
||||
double blockInteractRange = player.getBlockInteractionRange() + 1.0;
|
||||
return new Box(blockPos).squaredMagnitude(eyePos) < blockInteractRange * blockInteractRange;
|
||||
}else if(Main.config.expectedServerVersion == ModConfig.ExpectedServerVersion.All) {
|
||||
double blockInteractRange = player.getBlockInteractionRange() + 1.0;
|
||||
return eyePos.squaredDistanceTo(blockPos.toCenterPos()) <= 6.0 * 6.0 && new Box(blockPos).squaredMagnitude(eyePos) < blockInteractRange * blockInteractRange;
|
||||
}else {
|
||||
throw new NotImplementedException("ExpectedServerVersion Value not implemented: " + Main.config.expectedServerVersion.name());
|
||||
}
|
||||
}
|
||||
}
|
@ -23,6 +23,7 @@ import org.lwjgl.glfw.GLFW;
|
||||
import semmiedev.disc_jockey_revive.gui.hud.BlocksOverlay;
|
||||
import semmiedev.disc_jockey_revive.gui.hud.PlaybackProgressOverlay;
|
||||
import semmiedev.disc_jockey_revive.gui.screen.DiscJockeyScreen;
|
||||
import semmiedev.disc_jockey_revive.gui.screen.LiveDjScreen;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
@ -34,10 +35,13 @@ public class Main implements ClientModInitializer {
|
||||
public static final ArrayList<ClientTickEvents.StartWorldTick> TICK_LISTENERS = new ArrayList<>();
|
||||
public static final Previewer PREVIEWER = new Previewer();
|
||||
public static final SongPlayer SONG_PLAYER = new SongPlayer();
|
||||
public static final LiveDjPlayer LIVE_DJ_PLAYER = new LiveDjPlayer();
|
||||
|
||||
public static File songsFolder;
|
||||
public static ModConfig config;
|
||||
public static ConfigHolder<ModConfig> configHolder;
|
||||
public static KeyMappingManager keyMappingManager;
|
||||
|
||||
|
||||
@Override
|
||||
public void onInitializeClient() {
|
||||
@ -49,8 +53,12 @@ public class Main implements ClientModInitializer {
|
||||
|
||||
SongLoader.loadSongs();
|
||||
|
||||
keyMappingManager = new KeyMappingManager();
|
||||
|
||||
KeyBinding openScreenKeyBind = KeyBindingHelper.registerKeyBinding(new KeyBinding(MOD_ID+".key_bind.open_screen", InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_J, "key.category."+MOD_ID));
|
||||
|
||||
KeyBinding openLiveDjScreenKeyBind = KeyBindingHelper.registerKeyBinding(new KeyBinding(MOD_ID+".key_bind.open_live_dj_screen", InputUtil.Type.KEYSYM, GLFW.GLFW_KEY_UNKNOWN, "key.category."+MOD_ID));
|
||||
|
||||
ClientTickEvents.START_CLIENT_TICK.register(new ClientTickEvents.StartTick() {
|
||||
private ClientWorld prevWorld;
|
||||
|
||||
@ -59,6 +67,7 @@ public class Main implements ClientModInitializer {
|
||||
if (prevWorld != client.world) {
|
||||
PREVIEWER.stop();
|
||||
SONG_PLAYER.stop();
|
||||
LIVE_DJ_PLAYER.stopTuning();
|
||||
}
|
||||
prevWorld = client.world;
|
||||
|
||||
@ -70,6 +79,14 @@ public class Main implements ClientModInitializer {
|
||||
client.setScreen(new DiscJockeyScreen());
|
||||
}
|
||||
}
|
||||
if (openLiveDjScreenKeyBind.wasPressed()) {
|
||||
if (SongLoader.loadingSongs) {
|
||||
client.inGameHud.getChatHud().addMessage(Text.translatable(Main.MOD_ID+".still_loading").formatted(Formatting.RED));
|
||||
SongLoader.showToast = true;
|
||||
} else {
|
||||
client.setScreen(new LiveDjScreen());
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -84,6 +101,7 @@ public class Main implements ClientModInitializer {
|
||||
ClientLoginConnectionEvents.DISCONNECT.register((handler, client) -> {
|
||||
PREVIEWER.stop();
|
||||
SONG_PLAYER.stop();
|
||||
LIVE_DJ_PLAYER.stopTuning();
|
||||
});
|
||||
|
||||
HudRenderCallback.EVENT.register(new PlaybackProgressOverlay());
|
||||
|
@ -49,4 +49,7 @@ public class ModConfig implements ConfigData {
|
||||
|
||||
@ConfigEntry.Gui.Tooltip(count = 1)
|
||||
public boolean showHudProgressBar = true;
|
||||
|
||||
@ConfigEntry.Gui.Tooltip(count = 1)
|
||||
public boolean debugModeEnabled = false;
|
||||
}
|
||||
|
@ -0,0 +1,135 @@
|
||||
package semmiedev.disc_jockey_revive.gui;
|
||||
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.gui.DrawContext;
|
||||
import net.minecraft.client.gui.screen.narration.NarrationMessageBuilder;
|
||||
import net.minecraft.client.gui.screen.narration.NarrationPart;
|
||||
import net.minecraft.client.gui.widget.ButtonWidget;
|
||||
import net.minecraft.client.gui.widget.EntryListWidget;
|
||||
import net.minecraft.client.util.InputUtil;
|
||||
import net.minecraft.text.Text;
|
||||
import semmiedev.disc_jockey_revive.KeyMappingManager;
|
||||
import semmiedev.disc_jockey_revive.Main;
|
||||
import semmiedev.disc_jockey_revive.Note;
|
||||
import semmiedev.disc_jockey_revive.gui.screen.EditKeyMappingsScreen;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.Map;
|
||||
|
||||
public class KeyMappingListWidget extends EntryListWidget<KeyMappingListWidget.KeyMappingEntry> {
|
||||
|
||||
private final EditKeyMappingsScreen parentScreen;
|
||||
private boolean buttonsActive = true;
|
||||
|
||||
public KeyMappingListWidget(MinecraftClient client, int width, int height, int top, int itemHeight, EditKeyMappingsScreen parentScreen) {
|
||||
super(client, width, height, top, itemHeight);
|
||||
this.parentScreen = parentScreen;
|
||||
this.centerListVertically = false;
|
||||
}
|
||||
public void setMappings(Map<InputUtil.Key, Note> mappings) {
|
||||
this.clearEntries();
|
||||
for (Map.Entry<InputUtil.Key, Note> entry : mappings.entrySet()) {
|
||||
|
||||
this.addEntry(new KeyMappingEntry(entry.getKey(), entry.getValue(), this.parentScreen));
|
||||
}
|
||||
|
||||
this.children().sort(Comparator.comparing(entry -> entry.getKey().getTranslationKey()));
|
||||
|
||||
setButtonsActive(this.buttonsActive);
|
||||
}
|
||||
public void setButtonsActive(boolean active) {
|
||||
this.buttonsActive = active;
|
||||
|
||||
for (KeyMappingEntry entry : children()) {
|
||||
entry.setButtonsActive(active);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public int getRowWidth() {
|
||||
return this.width - 40;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getScrollbarX() {
|
||||
return this.width - 10;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void appendClickableNarrations(NarrationMessageBuilder builder) {
|
||||
}
|
||||
|
||||
public class KeyMappingEntry extends EntryListWidget.Entry<KeyMappingEntry> {
|
||||
private final InputUtil.Key key;
|
||||
private final Note note;
|
||||
private final EditKeyMappingsScreen screen;
|
||||
|
||||
private ButtonWidget changeButton;
|
||||
private ButtonWidget removeButton;
|
||||
|
||||
public KeyMappingEntry(InputUtil.Key key, Note note, EditKeyMappingsScreen screen) {
|
||||
this.key = key;
|
||||
this.note = note;
|
||||
this.screen = screen;
|
||||
}
|
||||
|
||||
public InputUtil.Key getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
public Note getNote() {
|
||||
return note;
|
||||
}
|
||||
|
||||
public void setButtonsActive(boolean active) {
|
||||
if (changeButton != null) changeButton.active = active;
|
||||
if (removeButton != null) removeButton.active = active;
|
||||
}
|
||||
@Override
|
||||
public void render(DrawContext context, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta) {
|
||||
MinecraftClient client = MinecraftClient.getInstance();
|
||||
int textY = y + (entryHeight - client.textRenderer.fontHeight) / 2;
|
||||
Text keyText = Text.translatable(key.getTranslationKey());
|
||||
context.drawTextWithShadow(client.textRenderer, keyText, x + 5, textY, 0xFFFFFF);
|
||||
String noteDisplayName = KeyMappingManager.getNoteDisplayName(note);
|
||||
context.drawTextWithShadow(client.textRenderer, noteDisplayName, x + 100, textY, 0xAAAAAA);
|
||||
int buttonWidth = 50;
|
||||
int buttonHeight = 18;
|
||||
int buttonY = y + (entryHeight - buttonHeight) / 2;
|
||||
int buttonX = x + entryWidth - buttonWidth - 5;
|
||||
changeButton = ButtonWidget.builder(Text.translatable(Main.MOD_ID + ".screen.edit_mappings.change"), button -> {
|
||||
screen.startWaitingForKeyPress(this);
|
||||
}).dimensions(buttonX - buttonWidth - 5, buttonY, buttonWidth, buttonHeight).build();
|
||||
changeButton.render(context, mouseX, mouseY, tickDelta);
|
||||
changeButton.active = buttonsActive;
|
||||
removeButton = ButtonWidget.builder(Text.translatable(Main.MOD_ID + ".screen.edit_mappings.remove"), button -> {
|
||||
screen.removeMapping(key);
|
||||
}).dimensions(buttonX, buttonY, buttonWidth, buttonHeight).build();
|
||||
removeButton.render(context, mouseX, mouseY, tickDelta);
|
||||
removeButton.active = buttonsActive;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mouseClicked(double mouseX, double mouseY, int button) {
|
||||
|
||||
if (changeButton.mouseClicked(mouseX, mouseY, button)) {
|
||||
return true;
|
||||
}
|
||||
if (removeButton.mouseClicked(mouseX, mouseY, button)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMouseOver(double mouseX, double mouseY) {
|
||||
return super.isMouseOver(mouseX, mouseY) ||
|
||||
(changeButton != null && changeButton.isMouseOver(mouseX, mouseY)) ||
|
||||
(removeButton != null && removeButton.isMouseOver(mouseX, mouseY));
|
||||
}
|
||||
public void appendClickableNarrations(NarrationMessageBuilder builder) {
|
||||
builder.put(NarrationPart.TITLE, Text.translatable(key.getTranslationKey()).append(" -> ").append(KeyMappingManager.getNoteDisplayName(note)));
|
||||
if (changeButton != null) changeButton.appendNarrations(builder);
|
||||
if (removeButton != null) removeButton.appendNarrations(builder);
|
||||
}
|
||||
}
|
||||
}
|
@ -29,7 +29,7 @@ public class PlaybackProgressOverlay implements HudRenderCallback {
|
||||
int screenHeight = context.getScaledWindowHeight();
|
||||
|
||||
int barX = screenWidth / 2 - PROGRESS_BAR_WIDTH / 2;
|
||||
int barY = screenHeight - 50;
|
||||
int barY = screenHeight - 55;
|
||||
|
||||
progressBarRenderer.renderProgressBar(
|
||||
context,
|
||||
|
@ -56,14 +56,12 @@ public class DiscJockeyScreen extends Screen {
|
||||
|
||||
private static final MutableText
|
||||
OPEN_FOLDER = Text.translatable(Main.MOD_ID+".screen.open_folder"),
|
||||
RELOAD = Text.translatable(Main.MOD_ID+".screen.reload");
|
||||
RELOAD = Text.translatable(Main.MOD_ID+".screen.reload"),
|
||||
LIVE_DJ = Text.translatable(Main.MOD_ID+".screen.live_dj").formatted(Formatting.GOLD);
|
||||
|
||||
private ButtonWidget folderUpButton, playModeButton;
|
||||
public SongFolder currentFolder;
|
||||
private PlayMode currentPlayMode = PlayMode.STOP_AFTER;
|
||||
private int progressBarWidth = 200; // 进度条宽度
|
||||
private int progressBarHeight = 5; // 进度条高度
|
||||
private int progressBarYOffset = 5; // 进度条Y偏移
|
||||
|
||||
private ProgressBarRenderer progressBarRenderer;
|
||||
|
||||
@ -126,14 +124,14 @@ public class DiscJockeyScreen extends Screen {
|
||||
if (isLargeScreen){
|
||||
buttonY = height - 30;
|
||||
} else {
|
||||
buttonY = height - 60;
|
||||
buttonY = height - 30; // awa
|
||||
}
|
||||
int centerX = width / 2;
|
||||
|
||||
// 上一首
|
||||
addDrawableChild(ButtonWidget.builder(Text.literal("◀◀◀"), button -> {
|
||||
Main.SONG_PLAYER.playPreviousSong();
|
||||
}).dimensions(centerX - 110, buttonY, 40, 20).build());
|
||||
}).dimensions(centerX - 100, buttonY, 40, 20).build());
|
||||
|
||||
// 播放暂停
|
||||
playButton = ButtonWidget.builder(PLAY, button -> {
|
||||
@ -168,7 +166,9 @@ public class DiscJockeyScreen extends Screen {
|
||||
int bottomY = height - 30;
|
||||
|
||||
|
||||
addDrawableChild(ButtonWidget.builder(Text.translatable(Main.MOD_ID+".screen.blocks"), button -> {
|
||||
// 音符盒
|
||||
if (isLargeScreen) {
|
||||
addDrawableChild(ButtonWidget.builder(Text.translatable(Main.MOD_ID + ".screen.blocks"), button -> {
|
||||
// TODO: 6/2/2022 Add an auto build mode
|
||||
if (BlocksOverlay.itemStacks == null) {
|
||||
SongListWidget.SongEntry entry = songListWidget.getSelectedSongOrNull();
|
||||
@ -206,6 +206,7 @@ public class DiscJockeyScreen extends Screen {
|
||||
client.setScreen(null);
|
||||
}
|
||||
}).dimensions(width - 110, height - 31, 100, 20).build());
|
||||
}
|
||||
|
||||
// 打开文件夹
|
||||
addDrawableChild(ButtonWidget.builder(OPEN_FOLDER, button -> {
|
||||
@ -238,12 +239,23 @@ public class DiscJockeyScreen extends Screen {
|
||||
|
||||
|
||||
// 重新加载
|
||||
if (isLargeScreen) {
|
||||
addDrawableChild(ButtonWidget.builder(RELOAD, button -> {
|
||||
SongLoader.loadSongs();
|
||||
client.setScreen(null);
|
||||
}).dimensions(120, bottomY, 100, 20).build());
|
||||
}
|
||||
|
||||
TextFieldWidget searchBar = new TextFieldWidget(textRenderer, 230, height - 31, 100, 20, Text.translatable(Main.MOD_ID+".screen.search"));
|
||||
// 即兴演奏
|
||||
if (isLargeScreen) {
|
||||
addDrawableChild(ButtonWidget.builder(LIVE_DJ, button -> {
|
||||
client.setScreen(new LiveDjScreen());
|
||||
}).dimensions(width - 220, bottomY, 100, 20).build());
|
||||
}
|
||||
|
||||
// 搜索框
|
||||
if (isLargeScreen) {
|
||||
TextFieldWidget searchBar = new TextFieldWidget(textRenderer, 230, height - 31, 100, 20, Text.translatable(Main.MOD_ID + ".screen.search"));
|
||||
searchBar.setChangedListener(query -> {
|
||||
query = query.toLowerCase().replaceAll("\\s", "");
|
||||
if (this.query.equals(query)) return;
|
||||
@ -251,6 +263,7 @@ public class DiscJockeyScreen extends Screen {
|
||||
shouldFilter = true;
|
||||
});
|
||||
addDrawableChild(searchBar);
|
||||
}
|
||||
|
||||
// Main.LOGGER.info("播放界面初始化完成!");
|
||||
|
||||
@ -328,7 +341,7 @@ public class DiscJockeyScreen extends Screen {
|
||||
}
|
||||
}
|
||||
|
||||
// 只有在songs目录或其子目录中才显示歌曲(原作者的💩跑我这了)
|
||||
// 只有在songs目录或其子目录中才显示歌曲(在搞了在搞了)
|
||||
if (isInSongsOrSubfolder) {
|
||||
// 歌曲条目
|
||||
List<Song> songsToShow = currentFolder == null ?
|
||||
|
@ -0,0 +1,146 @@
|
||||
package semmiedev.disc_jockey_revive.gui.screen;
|
||||
|
||||
import net.minecraft.client.gui.DrawContext;
|
||||
import net.minecraft.client.gui.screen.Screen;
|
||||
import net.minecraft.client.gui.widget.ButtonWidget;
|
||||
import net.minecraft.client.util.InputUtil;
|
||||
import net.minecraft.text.Text;
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
import semmiedev.disc_jockey_revive.Main;
|
||||
import semmiedev.disc_jockey_revive.Note;
|
||||
import semmiedev.disc_jockey_revive.gui.KeyMappingListWidget;
|
||||
|
||||
public class EditKeyMappingsScreen extends Screen {
|
||||
|
||||
private static final Text TITLE = Text.translatable(Main.MOD_ID + ".screen.edit_mappings.title");
|
||||
private static final Text ADD_MAPPING_BUTTON_TEXT = Text.translatable(Main.MOD_ID + ".screen.edit_mappings.add_mapping");
|
||||
private static final Text DONE_BUTTON_TEXT = Text.translatable("gui.done");
|
||||
private static final Text PRESS_KEY_INSTRUCTION = Text.translatable(Main.MOD_ID + ".screen.edit_mappings.press_key");
|
||||
|
||||
private final Screen parent;
|
||||
private KeyMappingListWidget mappingListWidget;
|
||||
private ButtonWidget addMappingButton;
|
||||
private ButtonWidget doneButton;
|
||||
|
||||
private boolean waitingForKeyPress = false;
|
||||
private KeyMappingListWidget.KeyMappingEntry entryToEdit = null;
|
||||
|
||||
public EditKeyMappingsScreen(Screen parent) {
|
||||
super(TITLE);
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void init() {
|
||||
super.init();
|
||||
|
||||
int listTop = 40;
|
||||
int listBottom = this.height - 50;
|
||||
int listHeight = listBottom - listTop;
|
||||
|
||||
mappingListWidget = new KeyMappingListWidget(this.client, this.width, listHeight, listTop, 20, this);
|
||||
addDrawableChild(mappingListWidget);
|
||||
refreshMappingList();
|
||||
int buttonWidth = 100;
|
||||
int buttonHeight = 20;
|
||||
int buttonY = this.height - 30;
|
||||
int buttonX = this.width / 2 - buttonWidth - 5;
|
||||
|
||||
addMappingButton = ButtonWidget.builder(ADD_MAPPING_BUTTON_TEXT, button -> {
|
||||
startWaitingForKeyPress(null);
|
||||
}).dimensions(buttonX, buttonY, buttonWidth, buttonHeight).build();
|
||||
addDrawableChild(addMappingButton);
|
||||
buttonX = this.width / 2 + 5;
|
||||
doneButton = ButtonWidget.builder(DONE_BUTTON_TEXT, button -> {
|
||||
Main.keyMappingManager.saveMappings();
|
||||
this.client.setScreen(this.parent);
|
||||
}).dimensions(buttonX, buttonY, buttonWidth, buttonHeight).build();
|
||||
addDrawableChild(doneButton);
|
||||
}
|
||||
|
||||
private void refreshMappingList() {
|
||||
|
||||
mappingListWidget.setMappings(Main.keyMappingManager.getMappings());
|
||||
}
|
||||
public void startWaitingForKeyPress(KeyMappingListWidget.KeyMappingEntry entry) {
|
||||
this.waitingForKeyPress = true;
|
||||
this.entryToEdit = entry;
|
||||
|
||||
addMappingButton.active = false;
|
||||
doneButton.active = false;
|
||||
mappingListWidget.setButtonsActive(false);
|
||||
}
|
||||
public void addNewMapping(InputUtil.Key key, Note note) {
|
||||
Main.keyMappingManager.setMapping(key, note);
|
||||
refreshMappingList();
|
||||
|
||||
}
|
||||
public void removeMapping(InputUtil.Key key) {
|
||||
Main.keyMappingManager.removeMapping(key);
|
||||
refreshMappingList();
|
||||
}
|
||||
public void stopWaitingForKeyPress() {
|
||||
this.waitingForKeyPress = false;
|
||||
this.entryToEdit = null;
|
||||
|
||||
addMappingButton.active = true;
|
||||
doneButton.active = true;
|
||||
mappingListWidget.setButtonsActive(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean keyPressed(int keyCode, int scanCode, int modifiers) {
|
||||
if (waitingForKeyPress) {
|
||||
if (keyCode == GLFW.GLFW_KEY_ESCAPE) {
|
||||
|
||||
stopWaitingForKeyPress();
|
||||
return true;
|
||||
}
|
||||
|
||||
InputUtil.Key pressedKey = InputUtil.fromKeyCode(keyCode, scanCode);
|
||||
|
||||
if (entryToEdit != null) {
|
||||
|
||||
Note note = entryToEdit.getNote();
|
||||
|
||||
Main.keyMappingManager.removeMapping(entryToEdit.getKey());
|
||||
|
||||
Main.keyMappingManager.setMapping(pressedKey, note);
|
||||
refreshMappingList();
|
||||
stopWaitingForKeyPress();
|
||||
} else {
|
||||
this.client.setScreen(new SelectNoteScreen(this, pressedKey));
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (mappingListWidget.keyPressed(keyCode, scanCode, modifiers)) {
|
||||
return true;
|
||||
}
|
||||
if (keyCode == GLFW.GLFW_KEY_ESCAPE) {
|
||||
Main.keyMappingManager.saveMappings();
|
||||
this.client.setScreen(this.parent);
|
||||
return true;
|
||||
}
|
||||
|
||||
return super.keyPressed(keyCode, scanCode, modifiers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(DrawContext context, int mouseX, int mouseY, float delta) {
|
||||
super.render(context, mouseX, mouseY, delta);
|
||||
context.drawCenteredTextWithShadow(textRenderer, TITLE, this.width / 2, 10, 0xFFFFFF);
|
||||
if (waitingForKeyPress) {
|
||||
|
||||
context.fill(0, 0, this.width, this.height, 0x80000000);
|
||||
context.drawCenteredTextWithShadow(textRenderer, PRESS_KEY_INSTRUCTION, this.width / 2, this.height / 2 - 10, 0xFFFFFF);
|
||||
}
|
||||
mappingListWidget.render(context, mouseX, mouseY, delta);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldPause() {
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
@ -0,0 +1,179 @@
|
||||
package semmiedev.disc_jockey_revive.gui.screen;
|
||||
|
||||
import me.shedaniel.cloth.clothconfig.shadowed.blue.endless.jankson.annotation.Nullable;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.enums.NoteBlockInstrument;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.gui.DrawContext;
|
||||
import net.minecraft.client.gui.screen.Screen;
|
||||
import net.minecraft.client.gui.widget.ButtonWidget;
|
||||
import net.minecraft.client.util.InputUtil;
|
||||
import net.minecraft.text.Text;
|
||||
import semmiedev.disc_jockey_revive.DebugLogger;
|
||||
import semmiedev.disc_jockey_revive.Main;
|
||||
import semmiedev.disc_jockey_revive.Note;
|
||||
import semmiedev.disc_jockey_revive.KeyMappingManager;
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class LiveDjScreen extends Screen {
|
||||
|
||||
private static final Text TITLE = Text.translatable(Main.MOD_ID + ".screen.live_dj.title");
|
||||
private static final Text INSTRUCTIONS = Text.translatable(Main.MOD_ID + ".screen.live_dj.instructions");
|
||||
private static final Text EDIT_MAPPINGS_BUTTON_TEXT = Text.translatable(Main.MOD_ID + ".screen.live_dj.edit_mappings");
|
||||
private static final Text START_TUNING_BUTTON_TEXT = Text.translatable(Main.MOD_ID + ".screen.live_dj.start_tuning");
|
||||
private static final Text NOT_TUNED_MESSAGE = Text.translatable(Main.MOD_ID + ".player.not_tuned").formatted(net.minecraft.util.Formatting.RED);
|
||||
private static final Text NOTE_BLOCK_MISSING_MESSAGE = Text.translatable(Main.MOD_ID + ".player.note_block_missing_live").formatted(net.minecraft.util.Formatting.RED);
|
||||
private static final Text TOO_FAR_MESSAGE = Text.translatable(Main.MOD_ID + ".player.to_far_live").formatted(net.minecraft.util.Formatting.RED);
|
||||
private static final Text RATE_LIMITED_MESSAGE = Text.translatable(Main.MOD_ID + ".player.rate_limited_live").formatted(net.minecraft.util.Formatting.YELLOW);
|
||||
private ButtonWidget startTuningButton;
|
||||
|
||||
public LiveDjScreen() {
|
||||
super(TITLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void init() {
|
||||
super.init();
|
||||
|
||||
int centerX = this.width / 2;
|
||||
int buttonWidth = 150;
|
||||
int buttonHeight = 20;
|
||||
int buttonY = this.height - 30;
|
||||
int margin = 5;
|
||||
addDrawableChild(ButtonWidget.builder(EDIT_MAPPINGS_BUTTON_TEXT, button -> {
|
||||
MinecraftClient.getInstance().setScreen(new EditKeyMappingsScreen(this));
|
||||
}).dimensions(centerX - buttonWidth - margin, buttonY, buttonWidth, buttonHeight).build());
|
||||
startTuningButton = ButtonWidget.builder(START_TUNING_BUTTON_TEXT, button -> {
|
||||
Main.LIVE_DJ_PLAYER.startTuning();
|
||||
}).dimensions(centerX + margin, buttonY, buttonWidth, buttonHeight).build();
|
||||
addDrawableChild(startTuningButton);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(DrawContext context, int mouseX, int mouseY, float delta) {
|
||||
super.render(context, mouseX, mouseY, delta);
|
||||
context.drawCenteredTextWithShadow(textRenderer, TITLE, this.width / 2, 10, 0xFFFFFF);
|
||||
context.drawCenteredTextWithShadow(textRenderer, INSTRUCTIONS, this.width / 2, 30, 0xFFFFFF);
|
||||
|
||||
Text tuningStatusText;
|
||||
if (Main.LIVE_DJ_PLAYER.getNoteBlocks() == null) {
|
||||
tuningStatusText = Text.translatable(Main.MOD_ID + ".player.discovering").formatted(net.minecraft.util.Formatting.YELLOW);
|
||||
startTuningButton.active = true;
|
||||
startTuningButton.visible = true;
|
||||
} else if (!Main.LIVE_DJ_PLAYER.isTuned()) {
|
||||
int totalNeeded = 0;
|
||||
if (Main.LIVE_DJ_PLAYER.getNoteBlocks() != null) {
|
||||
for (HashMap<Byte, net.minecraft.util.math.BlockPos> instrumentNotes : Main.LIVE_DJ_PLAYER.getNoteBlocks().values()) {
|
||||
if (instrumentNotes != null) {
|
||||
for (net.minecraft.util.math.BlockPos pos : instrumentNotes.values()) {
|
||||
if (pos != null) {
|
||||
totalNeeded++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
int tunedCount = 0;
|
||||
MinecraftClient client = MinecraftClient.getInstance();
|
||||
if (client.world != null && Main.LIVE_DJ_PLAYER.getNoteBlocks() != null) {
|
||||
for (HashMap<Byte, net.minecraft.util.math.BlockPos> instrumentNotes : Main.LIVE_DJ_PLAYER.getNoteBlocks().values()) {
|
||||
if (instrumentNotes != null) {
|
||||
for (Map.Entry<Byte, net.minecraft.util.math.BlockPos> entry : instrumentNotes.entrySet()) {
|
||||
net.minecraft.util.math.BlockPos pos = entry.getValue();
|
||||
Byte wantedNote = entry.getKey();
|
||||
if (pos != null) {
|
||||
if (client.world.getBlockState(pos).contains(net.minecraft.state.property.Properties.NOTE)) {
|
||||
int currentNote = client.world.getBlockState(pos).get(net.minecraft.state.property.Properties.NOTE);
|
||||
if (currentNote == wantedNote.byteValue()) {
|
||||
tunedCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (totalNeeded > 0) {
|
||||
tuningStatusText = Text.translatable(Main.MOD_ID + ".player.tuning_progress", tunedCount, totalNeeded).formatted(net.minecraft.util.Formatting.YELLOW);
|
||||
} else {
|
||||
tuningStatusText = Text.translatable(Main.MOD_ID + ".player.finding_blocks").formatted(net.minecraft.util.Formatting.YELLOW);
|
||||
}
|
||||
startTuningButton.active = true;
|
||||
startTuningButton.visible = true;
|
||||
|
||||
} else {
|
||||
tuningStatusText = Text.translatable(Main.MOD_ID + ".player.tuned").formatted(net.minecraft.util.Formatting.GREEN);
|
||||
startTuningButton.active = false;
|
||||
startTuningButton.visible = false;
|
||||
}
|
||||
context.drawCenteredTextWithShadow(textRenderer, tuningStatusText, this.width / 2, 50, 0xFFFFFF);
|
||||
|
||||
|
||||
if (!Main.LIVE_DJ_PLAYER.missingInstrumentBlocks.isEmpty()) {
|
||||
int yOffset = 70;
|
||||
context.drawCenteredTextWithShadow(textRenderer, Text.translatable(Main.MOD_ID+".player.invalid_note_blocks").formatted(net.minecraft.util.Formatting.RED), this.width / 2, yOffset, 0xFFFFFF);
|
||||
yOffset += 12;
|
||||
for (Map.Entry<Block, Integer> entry : Main.LIVE_DJ_PLAYER.missingInstrumentBlocks.entrySet()) {
|
||||
context.drawCenteredTextWithShadow(textRenderer, Text.literal(entry.getKey().getName().getString()+" × "+entry.getValue()).formatted(net.minecraft.util.Formatting.RED), this.width / 2, yOffset, 0xFFFFFF);
|
||||
yOffset += 10;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean keyPressed(int keyCode, int scanCode, int modifiers) {
|
||||
InputUtil.Key key = InputUtil.fromKeyCode(keyCode, scanCode);
|
||||
if (keyCode == GLFW.GLFW_KEY_ESCAPE) {
|
||||
this.close();
|
||||
return true;
|
||||
}
|
||||
Note note = Main.keyMappingManager.getNoteForKey(key);
|
||||
|
||||
if (note != null) {
|
||||
if (!Main.LIVE_DJ_PLAYER.isTuned()) {
|
||||
MinecraftClient.getInstance().inGameHud.getChatHud().addMessage(NOT_TUNED_MESSAGE);
|
||||
return true;
|
||||
}
|
||||
boolean played = Main.LIVE_DJ_PLAYER.playNoteBlock(note);
|
||||
if (!played) {
|
||||
@Nullable net.minecraft.util.math.BlockPos blockPos = null;
|
||||
if (Main.LIVE_DJ_PLAYER.getNoteBlocks() != null && Main.LIVE_DJ_PLAYER.getNoteBlocks().containsKey(note.instrument())) {
|
||||
blockPos = Main.LIVE_DJ_PLAYER.getNoteBlocks().get(note.instrument()).get(note.note());
|
||||
}
|
||||
|
||||
if (blockPos == null) {
|
||||
MinecraftClient.getInstance().inGameHud.getChatHud().addMessage(NOTE_BLOCK_MISSING_MESSAGE.copy().append(" (" + KeyMappingManager.getNoteDisplayName(note) + ")"));
|
||||
} else if (!Main.LIVE_DJ_PLAYER.canInteractWith(MinecraftClient.getInstance().player, blockPos)) {
|
||||
MinecraftClient.getInstance().inGameHud.getChatHud().addMessage(TOO_FAR_MESSAGE.copy().append(" (" + KeyMappingManager.getNoteDisplayName(note) + ")"));
|
||||
} else {
|
||||
MinecraftClient.getInstance().inGameHud.getChatHud().addMessage(RATE_LIMITED_MESSAGE);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return super.keyPressed(keyCode, scanCode, modifiers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
super.tick();
|
||||
if (startTuningButton != null) {
|
||||
startTuningButton.visible = !Main.LIVE_DJ_PLAYER.isTuned();
|
||||
startTuningButton.active = !Main.LIVE_DJ_PLAYER.isTuningActive();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldPause() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
super.close();
|
||||
Main.LIVE_DJ_PLAYER.stopTuning();
|
||||
}
|
||||
}
|
@ -0,0 +1,173 @@
|
||||
package semmiedev.disc_jockey_revive.gui.screen;
|
||||
|
||||
import net.minecraft.block.enums.NoteBlockInstrument;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.gui.DrawContext;
|
||||
import net.minecraft.client.gui.screen.Screen;
|
||||
import net.minecraft.client.gui.widget.ButtonWidget;
|
||||
import net.minecraft.client.gui.widget.CyclingButtonWidget;
|
||||
import net.minecraft.client.gui.widget.SliderWidget;
|
||||
import net.minecraft.client.util.InputUtil;
|
||||
import net.minecraft.sound.SoundCategory;
|
||||
import net.minecraft.sound.SoundEvent;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
import semmiedev.disc_jockey_revive.Main;
|
||||
import semmiedev.disc_jockey_revive.Note;
|
||||
import semmiedev.disc_jockey_revive.KeyMappingManager;
|
||||
|
||||
public class SelectNoteScreen extends Screen {
|
||||
|
||||
private static final Text TITLE = Text.translatable(Main.MOD_ID + ".screen.select_note.title");
|
||||
private static final Text INSTRUMENT_TEXT = Text.translatable(Main.MOD_ID + ".screen.select_note.instrument");
|
||||
private static final Text PITCH_TEXT = Text.translatable(Main.MOD_ID + ".screen.select_note.pitch");
|
||||
private static final Text DONE_BUTTON_TEXT = Text.translatable("gui.done");
|
||||
private static final Text CANCEL_BUTTON_TEXT = Text.translatable("gui.cancel");
|
||||
private static final Text PREVIEW_BUTTON_TEXT = Text.translatable(Main.MOD_ID + ".screen.select_note.preview");
|
||||
private final EditKeyMappingsScreen parent;
|
||||
private final InputUtil.Key keyToMap;
|
||||
|
||||
private NoteBlockInstrument selectedInstrument = NoteBlockInstrument.HARP;
|
||||
private int selectedPitch = 12;
|
||||
private CustomPitchSlider pitchSlider;
|
||||
|
||||
private ButtonWidget previewButton;
|
||||
|
||||
public SelectNoteScreen(EditKeyMappingsScreen parent, InputUtil.Key keyToMap) {
|
||||
super(TITLE);
|
||||
this.parent = parent;
|
||||
this.keyToMap = keyToMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void init() {
|
||||
super.init();
|
||||
|
||||
int centerX = this.width / 2;
|
||||
int startY = this.height / 2 - 50;
|
||||
int widgetWidth = 200;
|
||||
int widgetHeight = 20;
|
||||
int margin = 5;
|
||||
CyclingButtonWidget<NoteBlockInstrument> instrumentButton = CyclingButtonWidget.builder((NoteBlockInstrument instrument) -> Text.translatable("block.minecraft.note_block.instrument." + instrument.asString()))
|
||||
.values(NoteBlockInstrument.values())
|
||||
.initially(selectedInstrument)
|
||||
|
||||
.build(centerX - widgetWidth / 2, startY, widgetWidth, widgetHeight, INSTRUMENT_TEXT, (button, instrument) -> {
|
||||
this.selectedInstrument = instrument;
|
||||
updatePitchSliderDisplay();
|
||||
updatePreviewButton();
|
||||
});
|
||||
addDrawableChild(instrumentButton);
|
||||
pitchSlider = new CustomPitchSlider(centerX - widgetWidth / 2, startY + widgetHeight + margin, widgetWidth, widgetHeight, PITCH_TEXT, (selectedPitch / 24.0));
|
||||
updatePitchSliderDisplay();
|
||||
addDrawableChild(pitchSlider);
|
||||
previewButton = ButtonWidget.builder(PREVIEW_BUTTON_TEXT, button -> {
|
||||
playPreviewNote();
|
||||
}).dimensions(centerX - widgetWidth / 2, startY + (widgetHeight + margin) * 2, widgetWidth, widgetHeight).build();
|
||||
addDrawableChild(previewButton);
|
||||
int buttonWidth = 100;
|
||||
int buttonY = this.height - 30;
|
||||
int doneButtonX = centerX - buttonWidth - margin;
|
||||
addDrawableChild(ButtonWidget.builder(DONE_BUTTON_TEXT, button -> {
|
||||
Note selectedNote = new Note(selectedInstrument, (byte) selectedPitch);
|
||||
parent.addNewMapping(keyToMap, selectedNote);
|
||||
this.client.setScreen(this.parent);
|
||||
parent.stopWaitingForKeyPress();
|
||||
}).dimensions(doneButtonX, buttonY, buttonWidth, widgetHeight).build());
|
||||
int cancelButtonX = centerX + margin;
|
||||
addDrawableChild(ButtonWidget.builder(CANCEL_BUTTON_TEXT, button -> {
|
||||
this.client.setScreen(this.parent);
|
||||
parent.stopWaitingForKeyPress();
|
||||
}).dimensions(cancelButtonX, buttonY, buttonWidth, widgetHeight).build());
|
||||
}
|
||||
private class CustomPitchSlider extends SliderWidget {
|
||||
public CustomPitchSlider(int x, int y, int width, int height, Text text, double value) {
|
||||
super(x, y, width, height, text, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateMessage() {
|
||||
|
||||
this.setMessage(PITCH_TEXT.copy().append(": " + KeyMappingManager.getNoteDisplayName(new Note(selectedInstrument, (byte) selectedPitch))));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyValue() {
|
||||
selectedPitch = (int) Math.round(MathHelper.lerp(this.value, 0.0, 24.0));
|
||||
updateMessage();
|
||||
updatePreviewButton();
|
||||
}
|
||||
public void forceUpdateDisplay() {
|
||||
this.updateMessage();
|
||||
}
|
||||
}
|
||||
private void updatePitchSliderDisplay() {
|
||||
if (pitchSlider != null) {
|
||||
|
||||
pitchSlider.forceUpdateDisplay();
|
||||
}
|
||||
}
|
||||
|
||||
private void updatePreviewButton() {
|
||||
|
||||
}
|
||||
|
||||
private void playPreviewNote() {
|
||||
MinecraftClient client = MinecraftClient.getInstance();
|
||||
if (client.world != null && client.gameRenderer != null) {
|
||||
Vec3d pos = client.gameRenderer.getCamera().getPos();
|
||||
try {
|
||||
Note note = new Note(selectedInstrument, (byte) selectedPitch);
|
||||
|
||||
if (note.instrument().canBePitched()) {
|
||||
Identifier soundId = note.instrument().getSound().value().id();
|
||||
float pitchMultiplier = (float) Math.pow(2.0, (note.note() - 12) / 12.0);
|
||||
client.world.playSound(pos.x, pos.y, pos.z, SoundEvent.of(soundId), SoundCategory.RECORDS, 3.0f, pitchMultiplier, false);
|
||||
} else {
|
||||
|
||||
Identifier soundId = note.instrument().getSound().value().id();
|
||||
client.world.playSound(pos.x, pos.y, pos.z, SoundEvent.of(soundId), SoundCategory.RECORDS, 3.0f, 1.0f, false);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
Main.LOGGER.error("无法播放预览声音。", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void render(DrawContext context, int mouseX, int mouseY, float delta) {
|
||||
|
||||
super.render(context, mouseX, mouseY, delta);
|
||||
|
||||
context.drawCenteredTextWithShadow(textRenderer, TITLE, this.width / 2, 10, 0xFFFFFF);
|
||||
|
||||
context.drawCenteredTextWithShadow(textRenderer, Text.translatable(Main.MOD_ID + ".screen.select_note.mapping_key", Text.translatable(keyToMap.getTranslationKey())), this.width / 2, 30, 0xFFFFFF);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldPause() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean keyPressed(int keyCode, int scanCode, int modifiers) {
|
||||
|
||||
if (keyCode == GLFW.GLFW_KEY_ESCAPE) {
|
||||
this.client.setScreen(this.parent);
|
||||
parent.stopWaitingForKeyPress();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (keyCode == GLFW.GLFW_KEY_ENTER || keyCode == GLFW.GLFW_KEY_KP_ENTER) {
|
||||
Note selectedNote = new Note(selectedInstrument, (byte) selectedPitch);
|
||||
parent.addNewMapping(keyToMap, selectedNote);
|
||||
this.client.setScreen(this.parent);
|
||||
parent.stopWaitingForKeyPress();
|
||||
return true;
|
||||
}
|
||||
return super.keyPressed(keyCode, scanCode, modifiers);
|
||||
}
|
||||
}
|
@ -63,5 +63,35 @@
|
||||
"disc_jockey_revive.screen.reload": "Reload Songs",
|
||||
"disc_jockey_revive.screen.reloading": "Reloading songs...",
|
||||
"text.autoconfig.disc_jockey_revive.option.showHudProgressBar": "Show HUD progress bar",
|
||||
"text.autoconfig.disc_jockey_revive.option.showHudProgressBar.@Tooltip": "Show song playback progress bar in game。"
|
||||
"text.autoconfig.disc_jockey_revive.option.showHudProgressBar.@Tooltip": "Show song playback progress bar in game。",
|
||||
|
||||
"disc_jockey_revive.key_bind.open_live_dj_screen": "Open Live DJ Screen",
|
||||
"disc_jockey_revive.screen.live_dj": "Live DJ",
|
||||
"disc_jockey_revive.screen.live_dj.title": "Live DJ Performance",
|
||||
"disc_jockey_revive.screen.live_dj.instructions": "Press mapped keys to play notes. Tune note blocks first!",
|
||||
"disc_jockey_revive.screen.live_dj.edit_mappings": "Edit Key Mappings",
|
||||
"disc_jockey_revive.screen.live_dj.start_tuning": "Start Tuning",
|
||||
"disc_jockey_revive.screen.edit_mappings.title": "Edit Key Mappings",
|
||||
"disc_jockey_revive.screen.edit_mappings.add_mapping": "Add New Mapping",
|
||||
"disc_jockey_revive.screen.edit_mappings.change": "Change Key",
|
||||
"disc_jockey_revive.screen.edit_mappings.remove": "Remove",
|
||||
"disc_jockey_revive.screen.edit_mappings.press_key": "Press a key to map...",
|
||||
"disc_jockey_revive.screen.select_note.title": "Select Note for Key",
|
||||
"disc_jockey_revive.screen.select_note.instrument": "Instrument",
|
||||
"disc_jockey_revive.screen.select_note.pitch": "Pitch",
|
||||
"disc_jockey_revive.screen.select_note.preview": "Preview Note",
|
||||
"disc_jockey_revive.screen.select_note.mapping_key": "Mapping Key: %s",
|
||||
"disc_jockey_revive.player.not_tuned": "Note blocks are not tuned yet!",
|
||||
"disc_jockey_revive.player.note_block_missing_live": "Missing note block for this note!",
|
||||
"disc_jockey_revive.player.to_far_live": "You are too far from the note block!",
|
||||
"disc_jockey_revive.player.rate_limited_live": "Rate limited by server, try again soon.",
|
||||
"disc_jockey_revive.player.discovering": "Discovering note blocks...",
|
||||
"disc_jockey_revive.player.tuning_progress": "Tuning: %s/%s tuned",
|
||||
"disc_jockey_revive.player.finding_blocks": "Finding note blocks...",
|
||||
"disc_jockey_revive.player.retuning": "Retuning note blocks...",
|
||||
"disc_jockey_revive.player.tuning_started": "Tuning started. Please wait...",
|
||||
"disc_jockey_revive.player.invalid_state_tuning": "Cannot tune: Invalid game state or mode.",
|
||||
"disc_jockey_revive.player.tuned": "Tuning completed。",
|
||||
"text.autoconfig.disc_jockey_revive.option.debugModeEnabled": "Enable Debug Mode",
|
||||
"text.autoconfig.disc_jockey_revive.option.debugModeEnabled.@Tooltip": "Enables verbose logging for debugging purposes. \nKeep off unless troubleshooting. \nOf course, many debug-related parts have already been removed."
|
||||
}
|
@ -10,7 +10,7 @@
|
||||
"disc_jockey_revive.screen.drop_hint": "将歌曲文件拖入此窗口以添加",
|
||||
"disc_jockey_revive.screen.drop_confirm": "是否将以下歌曲添加到 Disc Jockey?",
|
||||
"disc_jockey_revive.player.invalid_note_blocks": "附近的音符盒配置不正确。缺失:",
|
||||
"disc_jockey_revive.player.invalid_game_mode": "无法在 %s 模式下播放",
|
||||
"disc_jockey_revive.player.invalid_game_mode": "无法在 %s 下播放",
|
||||
"disc_jockey_revive.player.to_far": "你距离太远了",
|
||||
"disc_jockey_revive.still_loading": "歌曲仍在加载中",
|
||||
"disc_jockey_revive.reloading": "正在重新加载所有歌曲",
|
||||
@ -57,10 +57,41 @@
|
||||
"disc_jockey_revive.screen.mode_single": "单曲循环",
|
||||
"disc_jockey_revive.screen.mode_list": "列表循环",
|
||||
"disc_jockey_revive.screen.mode_random": "随机播放",
|
||||
"disc_jockey_revive.screen.mode_stop": "播完停止","disc_jockey_revive.screen.open_folder": "打开文件夹",
|
||||
"disc_jockey_revive.screen.mode_stop": "播完停止",
|
||||
"disc_jockey_revive.screen.open_folder": "打开文件夹",
|
||||
"disc_jockey_revive.screen.open_folder_failed": "无法打开文件夹",
|
||||
"disc_jockey_revive.screen.reload": "重新加载",
|
||||
"disc_jockey_revive.screen.reloading": "正在重新加载...",
|
||||
"text.autoconfig.disc_jockey_revive.option.showHudProgressBar": "显示HUD进度条",
|
||||
"text.autoconfig.disc_jockey_revive.option.showHudProgressBar.@Tooltip": "在游戏界面显示歌曲播放进度条"
|
||||
"text.autoconfig.disc_jockey_revive.option.showHudProgressBar.@Tooltip": "在游戏界面显示歌曲播放进度条",
|
||||
|
||||
"disc_jockey_revive.key_bind.open_live_dj_screen": "打开即兴演奏界面",
|
||||
"disc_jockey_revive.screen.live_dj": "即兴演奏",
|
||||
"disc_jockey_revive.screen.live_dj.title": "即兴演奏",
|
||||
"disc_jockey_revive.screen.live_dj.instructions": "按下映射的按键来演奏音符。请先调音音符盒!",
|
||||
"disc_jockey_revive.screen.live_dj.edit_mappings": "编辑按键映射",
|
||||
"disc_jockey_revive.screen.live_dj.start_tuning": "开始调音",
|
||||
"disc_jockey_revive.screen.edit_mappings.title": "编辑按键映射",
|
||||
"disc_jockey_revive.screen.edit_mappings.add_mapping": "添加新映射",
|
||||
"disc_jockey_revive.screen.edit_mappings.change": "更改按键",
|
||||
"disc_jockey_revive.screen.edit_mappings.remove": "移除",
|
||||
"disc_jockey_revive.screen.edit_mappings.press_key": "按下要映射的按键...",
|
||||
"disc_jockey_revive.screen.select_note.title": "为按键选择音符",
|
||||
"disc_jockey_revive.screen.select_note.instrument": "乐器",
|
||||
"disc_jockey_revive.screen.select_note.pitch": "音高",
|
||||
"disc_jockey_revive.screen.select_note.preview": "试听音符",
|
||||
"disc_jockey_revive.screen.select_note.mapping_key": "映射按键:%s",
|
||||
"disc_jockey_revive.player.not_tuned": "音符盒尚未调音!",
|
||||
"disc_jockey_revive.player.note_block_missing_live": "缺少此音符的音符盒!",
|
||||
"disc_jockey_revive.player.to_far_live": "你离音符盒太远了!",
|
||||
"disc_jockey_revive.player.rate_limited_live": "服务器限速,请稍后再试。",
|
||||
"disc_jockey_revive.player.discovering": "正在发现音符盒...",
|
||||
"disc_jockey_revive.player.tuning_progress": "调音中:%s/%s 已调音",
|
||||
"disc_jockey_revive.player.finding_blocks": "正在寻找音符盒...",
|
||||
"disc_jockey_revive.player.retuning": "正在重新调音音符盒...",
|
||||
"disc_jockey_revive.player.tuning_started": "调音已开始。请稍候...",
|
||||
"disc_jockey_revive.player.invalid_state_tuning": "无法调音:游戏状态或模式无效。",
|
||||
"disc_jockey_revive.player.tuned": "调音完成。",
|
||||
"text.autoconfig.disc_jockey_revive.option.debugModeEnabled": "启用调试模式",
|
||||
"text.autoconfig.disc_jockey_revive.option.debugModeEnabled.@Tooltip": "启用详细日志输出以进行调试。\n除非排查问题,否则请保持关闭。\n当然,很多调试用的部分已经移除。"
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user