370 lines
17 KiB
Java
370 lines
17 KiB
Java
package me.rigamortis.seppuku.impl.module.world;
|
|
|
|
import me.rigamortis.seppuku.Seppuku;
|
|
import me.rigamortis.seppuku.api.event.EventStageable;
|
|
import me.rigamortis.seppuku.api.event.network.EventReceivePacket;
|
|
import me.rigamortis.seppuku.api.event.player.EventPlayerUpdate;
|
|
import me.rigamortis.seppuku.api.event.player.EventUpdateWalkingPlayer;
|
|
import me.rigamortis.seppuku.api.event.render.EventRender3D;
|
|
import me.rigamortis.seppuku.api.event.world.EventLoadWorld;
|
|
import me.rigamortis.seppuku.api.module.Module;
|
|
import me.rigamortis.seppuku.api.module.notebot.Note;
|
|
import me.rigamortis.seppuku.api.module.notebot.NotePlayer;
|
|
import me.rigamortis.seppuku.api.task.rotation.RotationTask;
|
|
import me.rigamortis.seppuku.api.util.MathUtil;
|
|
import me.rigamortis.seppuku.api.util.RenderUtil;
|
|
import me.rigamortis.seppuku.api.util.Timer;
|
|
import me.rigamortis.seppuku.api.value.Value;
|
|
import net.minecraft.client.Minecraft;
|
|
import net.minecraft.client.renderer.GlStateManager;
|
|
import net.minecraft.network.play.client.CPacketPlayerDigging;
|
|
import net.minecraft.network.play.server.SPacketBlockAction;
|
|
import net.minecraft.util.EnumFacing;
|
|
import net.minecraft.util.EnumHand;
|
|
import net.minecraft.util.math.AxisAlignedBB;
|
|
import net.minecraft.util.math.BlockPos;
|
|
import net.minecraft.util.math.Vec3d;
|
|
import team.stiff.pomelo.impl.annotated.handler.annotation.Listener;
|
|
|
|
import javax.sound.midi.MidiMessage;
|
|
import javax.sound.midi.Receiver;
|
|
import javax.sound.midi.ShortMessage;
|
|
import java.util.ArrayList;
|
|
import java.util.HashMap;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.stream.IntStream;
|
|
|
|
/**
|
|
* @author noil
|
|
*/
|
|
public final class NoteBotModule extends Module {
|
|
|
|
private final Value<BotState> state = new Value<BotState>("State", new String[]{"State", "s"}, "Current state of the note-bot.", BotState.IDLE);
|
|
private final Value<Mode> mode = new Value<Mode>("Mode", new String[]{"mod", "m"}, "Current mode of the note-bot.", Mode.NORMAL);
|
|
private final Value<Boolean> rotate = new Value<Boolean>("Rotate", new String[]{"rot", "r"}, "Rotate the player's head & body for each note-bot function.", false);
|
|
private final Value<Boolean> swing = new Value<Boolean>("Swing", new String[]{"swingarm", "armswing", "sa"}, "Swing the player's hand for each note-bot function.", false);
|
|
private final Value<Float> discoverDelay = new Value<Float>("DiscoverDelay", new String[]{"Discover Delay", "discover-delay", "ddelay", "ddel", "dd", "d"}, "Delay(ms) to wait between left clicks.", 50.0f, 0.0f, 1000.0f, 1.0f);
|
|
private final Value<Float> tuneDelay = new Value<Float>("TuneDelay", new String[]{"Tune Delay", "tune-delay", "tdelay", "tdel", "td"}, "Delay(ms) to wait between right clicks.", 200.0f, 0.0f, 1000.0f, 1.0f);
|
|
|
|
private final RotationTask rotationTask = new RotationTask("NoteBot", 2);
|
|
|
|
private BlockPos currentBlock;
|
|
|
|
private final int[] positionOffsets = new int[]{2, 1, 2};
|
|
|
|
private final NotePlayer notePlayer = new NotePlayer();
|
|
private final NoteReceiver receiver = new NoteReceiver();
|
|
|
|
private final Timer discoverTimer = new Timer();
|
|
private final Timer tuneTimer = new Timer();
|
|
private final Timer stateTimer = new Timer();
|
|
|
|
private final List<BlockPos> blocks = new ArrayList<>();
|
|
private final List<BlockPos> tunedBlocks = new ArrayList<>();
|
|
private final Map<BlockPos, Note> discoveredBlocks = new HashMap<>();
|
|
|
|
private final int BLOCK_AREA = 25;
|
|
private final Minecraft mc = Minecraft.getMinecraft();
|
|
|
|
public NoteBotModule() {
|
|
super("NoteBot", new String[]{"NoteBot+", "MusicBot", "MusicPlayer", "MidiPlayer", "MidiBot"}, "Play .midi files on a 5x5 grid of note-blocks.", "NONE", -1, ModuleType.WORLD);
|
|
}
|
|
|
|
@Override
|
|
public void onEnable() {
|
|
super.onEnable();
|
|
|
|
if (mc.world == null)
|
|
return;
|
|
|
|
IntStream.range(0, BLOCK_AREA).forEach(note -> {
|
|
int[] area = this.blockArea(note);
|
|
this.blocks.add(new BlockPos(area[0], area[1], area[2]));
|
|
});
|
|
|
|
if (this.mode.getValue().equals(Mode.NORMAL)) {
|
|
this.state.setEnumValue("DISCOVERING");
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onDisable() {
|
|
super.onDisable();
|
|
|
|
if (mc.world == null)
|
|
return;
|
|
|
|
this.clearData();
|
|
Seppuku.INSTANCE.getRotationManager().finishTask(this.rotationTask);
|
|
}
|
|
|
|
@Override
|
|
public String getMetaData() {
|
|
if (this.state.getValue().equals(BotState.PLAYING)) {
|
|
return this.state.getValue().name() + " " + this.getNotePlayer().getCurrentSongName();
|
|
}
|
|
|
|
return this.state.getValue().name();
|
|
}
|
|
|
|
@Listener
|
|
public void onLoadWorld(EventLoadWorld event) {
|
|
if (event.getWorld() != null) {
|
|
this.clearData();
|
|
this.toggle(); // toggle off
|
|
Seppuku.INSTANCE.logChat("\247rToggled\2477 " + this.getDisplayName() + " \247coff\247r, as you've loaded into a new world.");
|
|
}
|
|
}
|
|
|
|
@Listener
|
|
public void onReceivePacket(EventReceivePacket event) {
|
|
if (event.getStage() != EventStageable.EventStage.POST)
|
|
return;
|
|
|
|
if (!(event.getPacket() instanceof SPacketBlockAction))
|
|
return;
|
|
|
|
SPacketBlockAction packetBlockAction = (SPacketBlockAction) event.getPacket();
|
|
BlockPos position = packetBlockAction.getBlockPosition();
|
|
this.blocks.stream().filter(blockPos -> this.correctPosition(position, this.blocks.indexOf(blockPos))).forEach(blockPos -> {
|
|
final Note note = new Note(this.blocks.indexOf(blockPos), position, packetBlockAction.getData1(), packetBlockAction.getData2());
|
|
this.discoveredBlocks.put(blockPos, note);
|
|
|
|
if (!this.tunedBlocks.contains(blockPos) && this.blocks.indexOf(blockPos) == packetBlockAction.getData2()) {
|
|
this.tunedBlocks.add(blockPos);
|
|
}
|
|
|
|
if (!this.mode.getValue().equals(Mode.DEBUG) && !this.state.getValue().equals(BotState.PLAYING)) {
|
|
if (!this.state.getValue().equals(BotState.TUNING)) {
|
|
if (this.discoveredBlocks.size() != BLOCK_AREA) {
|
|
this.stateTimer.reset();
|
|
} else if (this.tunedBlocks.size() == BLOCK_AREA) {
|
|
this.state.setValue(BotState.IDLE);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
@Listener
|
|
public void onUpdate(EventPlayerUpdate event) {
|
|
if (mc.world == null || mc.player == null)
|
|
return;
|
|
|
|
if (mc.player.capabilities.isCreativeMode) {
|
|
if (this.rotationTask.isOnline())
|
|
Seppuku.INSTANCE.getRotationManager().finishTask(this.rotationTask);
|
|
|
|
return;
|
|
}
|
|
|
|
switch (event.getStage()) {
|
|
case PRE:
|
|
if (this.mode.getValue().equals(Mode.NORMAL)) {
|
|
if (this.state.getValue().equals(BotState.TUNING)) {
|
|
if (this.discoveredBlocks.size() == BLOCK_AREA && this.tunedBlocks.size() == BLOCK_AREA) {
|
|
this.state.setValue(BotState.IDLE);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (this.currentBlock == null && !this.state.getValue().equals(BotState.PLAYING)) {
|
|
this.blocks.stream().filter(blockPos -> (!this.discoveredBlocks.containsKey(blockPos) || !this.tunedBlocks.contains(blockPos))).forEach(blockPos -> {
|
|
final BlockPos workPos = new BlockPos(this.getPosition(this.blocks.indexOf(blockPos)));
|
|
if (!this.discoveredBlocks.containsKey(blockPos)) {
|
|
if (!this.mode.getValue().equals(Mode.DEBUG)) {
|
|
this.state.setValue(BotState.DISCOVERING);
|
|
}
|
|
this.setCurrentNoteBlock(workPos);
|
|
} else if (!this.tunedBlocks.contains(workPos)) {
|
|
if (!this.mode.getValue().equals(Mode.DEBUG)) {
|
|
if (this.stateTimer.passed(1000)) {
|
|
this.state.setValue(BotState.TUNING);
|
|
this.setCurrentNoteBlock(workPos);
|
|
}
|
|
} else {
|
|
this.setCurrentNoteBlock(workPos);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
break;
|
|
case POST:
|
|
if (this.rotationTask.isOnline() || !this.rotate.getValue()) {
|
|
final EnumFacing direction = EnumFacing.UP;
|
|
|
|
switch (this.state.getValue()) {
|
|
case IDLE:
|
|
if (this.rotationTask.isOnline()) {
|
|
Seppuku.INSTANCE.getRotationManager().finishTask(this.rotationTask);
|
|
}
|
|
break;
|
|
case DISCOVERING:
|
|
if (this.discoveredBlocks.size() != BLOCK_AREA) {
|
|
if (this.discoverTimer.passed(this.discoverDelay.getValue())) {
|
|
mc.player.connection.sendPacket(new CPacketPlayerDigging(CPacketPlayerDigging.Action.START_DESTROY_BLOCK, this.currentBlock, direction));
|
|
mc.player.connection.sendPacket(new CPacketPlayerDigging(CPacketPlayerDigging.Action.ABORT_DESTROY_BLOCK, this.currentBlock, direction));
|
|
if (this.swing.getValue()) {
|
|
mc.player.swingArm(EnumHand.MAIN_HAND);
|
|
}
|
|
this.discoveredBlocks.put(this.currentBlock, new Note(this.blocks.indexOf(this.currentBlock), this.currentBlock, 0, this.blocks.indexOf(this.currentBlock)));
|
|
this.currentBlock = null;
|
|
this.discoverTimer.reset();
|
|
}
|
|
}
|
|
break;
|
|
case TUNING:
|
|
if (!this.mode.getValue().equals(Mode.DEBUG)) {
|
|
if (this.discoveredBlocks.size() != BLOCK_AREA) {
|
|
this.state.setValue(BotState.DISCOVERING);
|
|
break;
|
|
}
|
|
}
|
|
if (this.tunedBlocks.size() != BLOCK_AREA && !this.tunedBlocks.contains(this.currentBlock)) {
|
|
if (this.tuneTimer.passed(this.tuneDelay.getValue())) {
|
|
mc.playerController.processRightClickBlock(mc.player, mc.world, this.currentBlock, direction, new Vec3d(0.5F, 0.5F, 0.5F), EnumHand.MAIN_HAND);
|
|
if (this.swing.getValue()) {
|
|
mc.player.swingArm(EnumHand.MAIN_HAND);
|
|
}
|
|
this.currentBlock = null;
|
|
this.tuneTimer.reset();
|
|
}
|
|
} else {
|
|
this.currentBlock = null;
|
|
}
|
|
break;
|
|
case PLAYING:
|
|
if (this.currentBlock != null) {
|
|
mc.player.connection.sendPacket(new CPacketPlayerDigging(CPacketPlayerDigging.Action.START_DESTROY_BLOCK, this.currentBlock, direction));
|
|
mc.player.connection.sendPacket(new CPacketPlayerDigging(CPacketPlayerDigging.Action.ABORT_DESTROY_BLOCK, this.currentBlock, direction));
|
|
if (this.swing.getValue()) {
|
|
mc.player.swingArm(EnumHand.MAIN_HAND);
|
|
}
|
|
this.currentBlock = null;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
@Listener
|
|
public void onRender3D(EventRender3D event) {
|
|
if (mc.player != null && mc.world != null && mc.getRenderViewEntity() != null) {
|
|
RenderUtil.begin3D();
|
|
for (int note = 0; note < BLOCK_AREA; note++) {
|
|
int[] area = this.blockArea(note);
|
|
final BlockPos pos = new BlockPos(area[0], area[1], area[2]);
|
|
|
|
float[] color = new float[]{128.0f, 128.0f, 128.0f};
|
|
|
|
if (this.tunedBlocks.contains(pos)) {
|
|
float mappedColor = (float) MathUtil.map(note, 0.0D, BLOCK_AREA, 0.0D, 255.0D);
|
|
color = new float[]{255.0F - mappedColor, mappedColor, 0.0F};
|
|
} else if (this.discoveredBlocks.containsKey(pos)) {
|
|
float mappedColor = (float) MathUtil.map(this.discoveredBlocks.get(pos).getPitch(), 0.0D, BLOCK_AREA, 0.0D, 255.0D);
|
|
color = new float[]{255.0F - mappedColor, mappedColor, 0.0F};
|
|
}
|
|
|
|
final AxisAlignedBB bb = new AxisAlignedBB(
|
|
pos.getX() - mc.getRenderManager().viewerPosX, pos.getY() + 1.0f - mc.getRenderManager().viewerPosY, pos.getZ() - mc.getRenderManager().viewerPosZ,
|
|
pos.getX() + 1.0f - mc.getRenderManager().viewerPosX, pos.getY() + 1.0f - mc.getRenderManager().viewerPosY, pos.getZ() + 1.0f - mc.getRenderManager().viewerPosZ);
|
|
GlStateManager.color(color[0] / 255.0F, color[1] / 255.0F, color[2] / 255.0F, 0.2F);
|
|
RenderUtil.drawFilledBox(bb);
|
|
//GlStateManager.color(color[0] / 255.0F, color[1] / 255.0F, color[2] / 255.0F, 0.6F);
|
|
//RenderUtil.drawBoundingBox(bb, 1.0f);
|
|
}
|
|
RenderUtil.end3D();
|
|
}
|
|
}
|
|
|
|
private int[] blockArea(int index) {
|
|
int[] positions = {(int) Math.floor(mc.player.posX) - this.positionOffsets[0], (int) Math.floor(mc.player.posY) - this.positionOffsets[1], (int) Math.floor(mc.player.posZ) - this.positionOffsets[2]};
|
|
return new int[]{positions[0] + index % 5, positions[1], positions[2] + index / 5};
|
|
}
|
|
|
|
private void lookAtPosition(BlockPos position) {
|
|
Seppuku.INSTANCE.getRotationManager().startTask(this.rotationTask);
|
|
if (this.rotationTask.isOnline()) {
|
|
final float[] angle = MathUtil.calcAngle(mc.player.getPositionEyes(mc.getRenderPartialTicks()), new Vec3d(position.getX() + 0.5f, position.getY() + 0.5f, position.getZ() + 0.5f));
|
|
Seppuku.INSTANCE.getRotationManager().setPlayerRotations(angle[0], angle[1]);
|
|
}
|
|
}
|
|
|
|
private BlockPos getPosition(int note) {
|
|
int[] blocks = this.blockArea(note);
|
|
return new BlockPos(blocks[0], blocks[1], blocks[2]);
|
|
}
|
|
|
|
private boolean correctPosition(BlockPos blockPos, int index) {
|
|
int[] blocks = this.blockArea(index);
|
|
return (blockPos.getX() == blocks[0] && blockPos
|
|
.getY() == blocks[1] && blockPos
|
|
.getZ() == blocks[2]);
|
|
}
|
|
|
|
private void setCurrentNoteBlock(BlockPos pos) {
|
|
this.currentBlock = new BlockPos(pos);
|
|
if (this.rotate.getValue()) {
|
|
this.lookAtPosition(pos);
|
|
}
|
|
}
|
|
|
|
private void clearData() {
|
|
this.currentBlock = null;
|
|
this.discoveredBlocks.clear();
|
|
if (!this.mode.getValue().equals(Mode.DEBUG)) { // is not debug, so let's wipe our previously tuned blocks data
|
|
this.tunedBlocks.clear();
|
|
}
|
|
this.blocks.clear();
|
|
this.notePlayer.end();
|
|
}
|
|
|
|
public enum BotState {
|
|
IDLE, DISCOVERING, TUNING, PLAYING;
|
|
}
|
|
|
|
public enum Mode {
|
|
NORMAL, DEBUG
|
|
}
|
|
|
|
public Value<BotState> getState() {
|
|
return state;
|
|
}
|
|
|
|
public NotePlayer getNotePlayer() {
|
|
return notePlayer;
|
|
}
|
|
|
|
public NoteReceiver getReceiver() {
|
|
return receiver;
|
|
}
|
|
|
|
public class NoteReceiver implements Receiver {
|
|
@Override
|
|
public void send(MidiMessage midiMessage, long l) {
|
|
if (midiMessage instanceof ShortMessage) {
|
|
ShortMessage shortMessage = (ShortMessage) midiMessage;
|
|
if (shortMessage.getCommand() == ShortMessage.NOTE_ON) {
|
|
int key = shortMessage.getData1() - 6;
|
|
int octave = (key / 12) - 1;
|
|
int note = key > 12 ? key % 24 : key % 12;
|
|
int velocity = shortMessage.getData2();
|
|
if (velocity > 0) {
|
|
NoteBotModule.this.setCurrentNoteBlock(new BlockPos(getPosition(note)));
|
|
//mc.player.connection.sendPacket(new CPacketPlayerDigging(CPacketPlayerDigging.Action.START_DESTROY_BLOCK, playPos, EnumFacing.UP));
|
|
//mc.player.connection.sendPacket(new CPacketPlayerDigging(CPacketPlayerDigging.Action.ABORT_DESTROY_BLOCK, playPos, EnumFacing.UP));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void close() {
|
|
|
|
}
|
|
}
|
|
}
|