seppuku/src/main/java/me/rigamortis/seppuku/impl/module/world/NoteBotModule.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() {
}
}
}