Added a couple new features

This commit is contained in:
Semmieboy YT 2022-05-31 15:10:50 +02:00
parent 10e93435f1
commit 79dc952732
14 changed files with 193 additions and 91 deletions

View File

@ -1,37 +0,0 @@
package semmieboy_yt.disc_jockey;
import net.fabricmc.fabric.api.client.command.v1.ClientCommandManager;
import net.fabricmc.fabric.api.client.command.v1.FabricClientCommandSource;
import net.minecraft.client.MinecraftClient;
import net.minecraft.text.TranslatableText;
import semmieboy_yt.disc_jockey.gui.screen.DiscJockeyScreen;
import static net.fabricmc.fabric.api.client.command.v1.ClientCommandManager.literal;
public class DiscjockeyCommand {
public static void register() {
ClientCommandManager.DISPATCHER.register(
literal("discjockey")
.executes(context -> {
FabricClientCommandSource source = context.getSource();
if (SongLoader.loadingSongs) {
source.sendError(new TranslatableText(Main.MOD_ID+".still_loading"));
} else {
MinecraftClient client = source.getClient();
client.send(() -> client.setScreen(new DiscJockeyScreen()));
}
return 1;
})
.then(literal("reload")
.executes(context -> {
if (SongLoader.loadingSongs) {
context.getSource().sendError(new TranslatableText(Main.MOD_ID+".still_loading"));
} else {
SongLoader.loadSongs();
}
return 1;
})
)
);
}
}

View File

@ -1,4 +1,4 @@
package semmieboy_yt.disc_jockey;
package semmiedev.disc_jockey;
import java.io.EOFException;
import java.io.IOException;

View File

@ -0,0 +1,90 @@
package semmiedev.disc_jockey;
import com.google.common.base.Predicate;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.suggestion.SuggestionProvider;
import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager;
import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource;
import net.minecraft.client.MinecraftClient;
import net.minecraft.command.CommandSource;
import net.minecraft.command.argument.EntityArgumentType;
import net.minecraft.server.command.PlaceCommand;
import net.minecraft.text.Text;
import semmiedev.disc_jockey.gui.screen.DiscJockeyScreen;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.function.Function;
import java.util.stream.Collectors;
import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.argument;
import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.literal;
public class DiscjockeyCommand {
public static void register() {
ClientCommandManager.DISPATCHER.register(
// TODO: 5/30/2022 Add play/stop sub-commands
literal("discjockey")
.executes(context -> {
FabricClientCommandSource source = context.getSource();
if (!isLoading(context)) {
MinecraftClient client = source.getClient();
client.send(() -> client.setScreen(new DiscJockeyScreen()));
return 1;
}
return 0;
})
.then(literal("reload")
.executes(context -> {
if (!isLoading(context)) {
context.getSource().sendFeedback(Text.translatable(Main.MOD_ID+".reloading"));
SongLoader.loadSongs();
return 1;
}
return 0;
})
)
.then(literal("play")
.then(argument("song", StringArgumentType.greedyString())
.suggests((context, builder) -> CommandSource.suggestMatching(SongLoader.SONG_SUGGESTIONS, builder))
.executes(context -> {
if (!isLoading(context)) {
String songName = StringArgumentType.getString(context, "song");
Optional<Song> song = SongLoader.SONGS.stream().filter(input -> input.displayName.equals(songName)).findAny();
if (song.isPresent()) {
Main.SONG_PLAYER.start(song.get());
return 1;
}
context.getSource().sendError(Text.translatable(Main.MOD_ID+".song_not_found", songName));
return 0;
}
return 0;
})
)
)
.then(literal("stop")
.executes(context -> {
if (Main.SONG_PLAYER.running) {
Main.SONG_PLAYER.stop();
context.getSource().sendFeedback(Text.translatable(Main.MOD_ID+".stopped_playing", Main.SONG_PLAYER.song));
return 1;
}
context.getSource().sendError(Text.translatable(Main.MOD_ID+".not_playing"));
return 0;
})
)
);
}
private static boolean isLoading(CommandContext<FabricClientCommandSource> context) {
if (SongLoader.loadingSongs) {
context.getSource().sendError(Text.translatable(Main.MOD_ID+".still_loading"));
return true;
}
return false;
}
}

View File

@ -1,4 +1,4 @@
package semmieboy_yt.disc_jockey;
package semmiedev.disc_jockey;
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
@ -7,15 +7,19 @@ import net.fabricmc.fabric.api.client.rendering.v1.HudRenderCallback;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.text.MutableText;
import net.minecraft.text.Text;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import semmieboy_yt.disc_jockey.gui.hud.BlocksOverlay;
import semmiedev.disc_jockey.gui.hud.BlocksOverlay;
import java.io.File;
import java.util.ArrayList;
// TODO: 5/31/2022 Add key bind to open disc jockey screen
public class Main implements ClientModInitializer {
public static final String MOD_ID = "disc_jockey";
public static final MutableText NAME = Text.literal("Disc Jockey");
public static final Logger LOGGER = LogManager.getLogger("Disc Jockey");
public static final ArrayList<ClientTickEvents.StartWorldTick> TICK_LISTENERS = new ArrayList<>();
public static final Previewer PREVIEWER = new Previewer();

View File

@ -1,4 +1,4 @@
package semmieboy_yt.disc_jockey;
package semmiedev.disc_jockey;
import net.minecraft.block.Block;
import net.minecraft.block.Blocks;

View File

@ -1,4 +1,4 @@
package semmieboy_yt.disc_jockey;
package semmiedev.disc_jockey;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
import net.minecraft.client.MinecraftClient;

View File

@ -1,6 +1,6 @@
package semmieboy_yt.disc_jockey;
package semmiedev.disc_jockey;
import semmieboy_yt.disc_jockey.gui.SongListWidget;
import semmiedev.disc_jockey.gui.SongListWidget;
import java.util.ArrayList;
@ -10,7 +10,7 @@ public class Song {
public long[] notes = new long[0];
public short length, height, tempo, loopStartTick;
public String fileName, name, author, originalAuthor, description;
public String fileName, name, author, originalAuthor, description, displayName;
public byte autoSaving, autoSavingDuration, timeSignature, vanillaInstrumentCount, formatVersion, loop, maxLoopCount;
public int minutesSpent, leftClicks, rightClicks, blocksAdded, blocksRemoved;
public String importFileName;

View File

@ -1,14 +1,19 @@
package semmieboy_yt.disc_jockey;
package semmiedev.disc_jockey;
import semmieboy_yt.disc_jockey.gui.SongListWidget;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.toast.SystemToast;
import net.minecraft.text.Text;
import semmiedev.disc_jockey.gui.SongListWidget;
import java.io.File;
import java.io.FileInputStream;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Arrays;
public class SongLoader {
public static final ArrayList<Song> SONGS = new ArrayList<>();
public static final ArrayList<String> SONG_SUGGESTIONS = new ArrayList<>();
public static volatile boolean loadingSongs;
public static void loadSongs() {
@ -16,10 +21,12 @@ public class SongLoader {
new Thread(() -> {
loadingSongs = true;
SONGS.clear();
SONG_SUGGESTIONS.clear();
SONG_SUGGESTIONS.add("Songs are loading, please wait");
for (File file : Main.songsFolder.listFiles()) {
if (file.isFile()) {
try {
BinaryReader reader = new BinaryReader(new FileInputStream(file));
BinaryReader reader = new BinaryReader(Files.newInputStream(file.toPath()));
Song song = new Song();
song.fileName = file.getName();
@ -55,7 +62,8 @@ public class SongLoader {
song.loopStartTick = reader.readShort();
}
song.entry = new SongListWidget.SongEntry(song.name.replaceAll("\\s", "").isEmpty() ? song.fileName : song.name+" ("+song.fileName+")", SONGS.size());
song.displayName = song.name.replaceAll("\\s", "").isEmpty() ? song.fileName : song.name+" ("+song.fileName+")";
song.entry = new SongListWidget.SongEntry(song.displayName, SONGS.size());
song.searchableFileName = song.fileName.toLowerCase().replaceAll("\\s", "");
song.searchableName = song.name.toLowerCase().replaceAll("\\s", "");
@ -97,6 +105,8 @@ public class SongLoader {
}
}
}
for (Song song : SONGS) SONG_SUGGESTIONS.add(song.displayName);
SystemToast.add(MinecraftClient.getInstance().getToastManager(), SystemToast.Type.PACK_LOAD_FAILURE, Main.NAME, Text.translatable(Main.MOD_ID+".loading_done"));
loadingSongs = false;
}).start();
}

View File

@ -1,4 +1,4 @@
package semmieboy_yt.disc_jockey;
package semmiedev.disc_jockey;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
import net.minecraft.block.Block;
@ -11,8 +11,7 @@ import net.minecraft.client.network.ClientPlayerEntity;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.network.packet.c2s.play.PlayerMoveC2SPacket;
import net.minecraft.state.property.Properties;
import net.minecraft.text.LiteralText;
import net.minecraft.text.TranslatableText;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
import net.minecraft.util.Hand;
import net.minecraft.util.hit.BlockHitResult;
@ -27,15 +26,16 @@ import java.util.HashMap;
public class SongPlayer implements ClientTickEvents.StartWorldTick {
public boolean running;
public Song song;
private int index;
private float tick;
private Song song;
private HashMap<Instrument, HashMap<Byte, BlockPos>> noteBlocks = null;
private boolean tuned;
private int tuneDelay = 5;
public void start(Song song) {
if (running) stop();
this.song = song;
Main.TICK_LISTENERS.add(this);
running = true;
@ -85,7 +85,7 @@ public class SongPlayer implements ClientTickEvents.StartWorldTick {
missingNotes.removeAll(capturedNotes);
if (!missingNotes.isEmpty()) {
ChatHud chatHud = MinecraftClient.getInstance().inGameHud.getChatHud();
chatHud.addMessage(new TranslatableText(Main.MOD_ID+".player.invalid_note_blocks").formatted(Formatting.RED));
chatHud.addMessage(Text.translatable(Main.MOD_ID+".player.invalid_note_blocks").formatted(Formatting.RED));
HashMap<Block, Integer> missing = new HashMap<>();
for (Note note : missingNotes) {
@ -95,7 +95,7 @@ public class SongPlayer implements ClientTickEvents.StartWorldTick {
missing.put(block, got + 1);
}
missing.forEach((block, integer) -> chatHud.addMessage(new LiteralText(block.getName().getString()+" × "+integer).formatted(Formatting.RED)));
missing.forEach((block, integer) -> chatHud.addMessage(Text.literal(block.getName().getString()+" × "+integer).formatted(Formatting.RED)));
stop();
}
} else if (!tuned) {
@ -114,12 +114,12 @@ public class SongPlayer implements ClientTickEvents.StartWorldTick {
if (blockState.get(Properties.NOTE) != note.note) {
if (client.player.getEyePos().squaredDistanceTo(Vec3d.ofCenter(blockPos, 0.5)) >= 4.5 * 4.5) {
stop();
client.inGameHud.getChatHud().addMessage(new TranslatableText(Main.MOD_ID+".player.to_far").formatted(Formatting.RED));
client.inGameHud.getChatHud().addMessage(Text.translatable(Main.MOD_ID+".player.to_far").formatted(Formatting.RED));
return;
}
Vec3d unit = Vec3d.ofCenter(blockPos, 0.5).subtract(client.player.getEyePos()).normalize();
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));
client.interactionManager.interactBlock(client.player, world, Hand.MAIN_HAND, new BlockHitResult(Vec3d.of(blockPos), Direction.UP, blockPos, false));
client.interactionManager.interactBlock(client.player, Hand.MAIN_HAND, new BlockHitResult(Vec3d.of(blockPos), Direction.UP, blockPos, false));
client.player.swingHand(Hand.MAIN_HAND);
tuned = false;
tuneDelay = 5;
@ -135,7 +135,7 @@ public class SongPlayer implements ClientTickEvents.StartWorldTick {
MinecraftClient client = MinecraftClient.getInstance();
GameMode gameMode = client.interactionManager.getCurrentGameMode();
if (!gameMode.isSurvivalLike()) {
client.inGameHud.getChatHud().addMessage(new TranslatableText(Main.MOD_ID+".player.invalid_game_mode", gameMode.getTranslatableName()).formatted(Formatting.RED));
client.inGameHud.getChatHud().addMessage(Text.translatable(Main.MOD_ID+".player.invalid_game_mode", gameMode.getTranslatableName()).formatted(Formatting.RED));
stop();
return;
}
@ -143,13 +143,15 @@ public class SongPlayer implements ClientTickEvents.StartWorldTick {
long note = song.notes[index];
if ((short)note == Math.round(tick)) {
BlockPos blockPos = noteBlocks.get(Note.INSTRUMENTS[(byte)(note >> Note.INSTRUMENT_SHIFT)]).get((byte)(note >> Note.NOTE_SHIFT));
// TODO: 5/30/2022 Check if the cube is inside of a sphere with a centre of 4.5 centered on the eye pos instead
if (client.player.getEyePos().squaredDistanceTo(Vec3d.ofCenter(blockPos, 0.5)) >= 4.5 * 4.5) {
stop();
client.inGameHud.getChatHud().addMessage(new TranslatableText(Main.MOD_ID+".player.to_far").formatted(Formatting.RED));
client.inGameHud.getChatHud().addMessage(Text.translatable(Main.MOD_ID+".player.to_far").formatted(Formatting.RED));
return;
}
Vec3d unit = Vec3d.ofCenter(blockPos, 0.5).subtract(client.player.getEyePos()).normalize();
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));
// TODO: 5/30/2022 Check if the block needs tuning
client.interactionManager.attackBlock(blockPos, Direction.UP);
client.player.swingHand(Hand.MAIN_HAND);

View File

@ -1,10 +1,14 @@
package semmieboy_yt.disc_jockey.gui;
package semmiedev.disc_jockey.gui;
import com.mojang.blaze3d.systems.RenderSystem;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.ingame.CraftingScreen;
import net.minecraft.client.gui.screen.narration.NarrationMessageBuilder;
import net.minecraft.client.gui.widget.EntryListWidget;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.util.Identifier;
import org.jetbrains.annotations.Nullable;
import semmiedev.disc_jockey.Main;
public class SongListWidget extends EntryListWidget<SongListWidget.SongEntry> {
public SongListWidget(MinecraftClient client, int width, int height, int top, int bottom, int itemHeight) {
@ -35,14 +39,18 @@ public class SongListWidget extends EntryListWidget<SongListWidget.SongEntry> {
}
public static class SongEntry extends Entry<SongEntry> {
private static final Identifier ICONS = new Identifier(Main.MOD_ID, "textures/gui/icons.png");
public final int index;
public boolean selected;
public boolean selected, favorite;
public SongListWidget songListWidget;
private final String name;
private final MinecraftClient client = MinecraftClient.getInstance();
private int x, y, entryWidth, entryHeight;
public SongEntry(String name, int index) {
this.name = name;
this.index = index;
@ -50,18 +58,31 @@ public class SongListWidget extends EntryListWidget<SongListWidget.SongEntry> {
@Override
public void render(MatrixStack matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta) {
this.x = x; this.y = y; this.entryWidth = entryWidth; this.entryHeight = entryHeight;
if (selected) {
fill(matrices, x, y, x + entryWidth, y + entryHeight, 0xFFFFFF);
fill(matrices, x + 1, y + 1, x + entryWidth - 1, y + entryHeight - 1, 0x000000);
}
drawCenteredText(matrices, client.textRenderer, name, x + entryWidth / 2, y + 5, selected ? 0xFFFFFF : 0x808080);
RenderSystem.setShaderTexture(0, ICONS);
drawTexture(matrices, x + 2, y + 2, (favorite ? 26 : 0) + (isOverFavoriteButton(mouseX, mouseY) ? 13 : 0), 0, 13, 12, 52, 12);
}
@Override
public boolean mouseClicked(double mouseX, double mouseY, int button) {
// it always gets clicked on
if (isOverFavoriteButton(mouseX, mouseY)) {
favorite = !favorite;
return true;
}
songListWidget.setSelected(this);
return true;
}
private boolean isOverFavoriteButton(double mouseX, double mouseY) {
return mouseX > x + 2 && mouseX < x + 15 && mouseY > y + 2 && mouseY < y + 14;
}
}
}

View File

@ -1,4 +1,4 @@
package semmieboy_yt.disc_jockey.gui.hud;
package semmiedev.disc_jockey.gui.hud;
import net.minecraft.block.Blocks;
import net.minecraft.client.MinecraftClient;

View File

@ -1,45 +1,46 @@
package semmieboy_yt.disc_jockey.gui.screen;
package semmiedev.disc_jockey.gui.screen;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.widget.ButtonWidget;
import net.minecraft.client.gui.widget.TextFieldWidget;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.item.ItemStack;
import net.minecraft.text.TranslatableText;
import semmieboy_yt.disc_jockey.Main;
import semmieboy_yt.disc_jockey.Note;
import semmieboy_yt.disc_jockey.Song;
import semmieboy_yt.disc_jockey.SongLoader;
import semmieboy_yt.disc_jockey.gui.SongListWidget;
import semmieboy_yt.disc_jockey.gui.hud.BlocksOverlay;
import net.minecraft.text.MutableText;
import net.minecraft.text.Text;
import semmiedev.disc_jockey.Main;
import semmiedev.disc_jockey.Note;
import semmiedev.disc_jockey.Song;
import semmiedev.disc_jockey.SongLoader;
import semmiedev.disc_jockey.gui.SongListWidget;
import semmiedev.disc_jockey.gui.hud.BlocksOverlay;
import java.util.Arrays;
public class DiscJockeyScreen extends Screen {
private static final TranslatableText
SELECT_SONG = new TranslatableText(Main.MOD_ID+".screen.select_song"),
PLAY = new TranslatableText(Main.MOD_ID+".screen.play"),
PLAY_STOP = new TranslatableText(Main.MOD_ID+".screen.play.stop"),
PREVIEW = new TranslatableText(Main.MOD_ID+".screen.preview"),
PREVIEW_STOP = new TranslatableText(Main.MOD_ID+".screen.preview.stop")
private static final MutableText
SELECT_SONG = Text.translatable(Main.MOD_ID+".screen.select_song"),
PLAY = Text.translatable(Main.MOD_ID+".screen.play"),
PLAY_STOP = Text.translatable(Main.MOD_ID+".screen.play.stop"),
PREVIEW = Text.translatable(Main.MOD_ID+".screen.preview"),
PREVIEW_STOP = Text.translatable(Main.MOD_ID+".screen.preview.stop")
;
private SongListWidget songListWidget;
private ButtonWidget playButton, previewButton;
private boolean shouldFilter;
private String query;
private String query = "";
public DiscJockeyScreen() {
super(new TranslatableText(Main.MOD_ID+".screen.title"));
super(Main.NAME);
}
@Override
protected void init() {
shouldFilter = true;
songListWidget = new SongListWidget(client, width, height, 32, height - 64, 20);
addDrawableChild(songListWidget);
for (int i = 0; i < SongLoader.SONGS.size(); i++) {
Song song = SongLoader.SONGS.get(i);
songListWidget.children().add(song.entry);
song.entry.songListWidget = songListWidget;
if (song.entry.selected) songListWidget.setSelected(song.entry);
}
@ -67,7 +68,7 @@ public class DiscJockeyScreen extends Screen {
});
addDrawableChild(previewButton);
addDrawableChild(new ButtonWidget(width / 2 + 60, height - 61, 100, 20, new TranslatableText(Main.MOD_ID+".screen.blocks"), button -> {
addDrawableChild(new ButtonWidget(width / 2 + 60, height - 61, 100, 20, Text.translatable(Main.MOD_ID+".screen.blocks"), button -> {
if (BlocksOverlay.itemStacks == null) {
SongListWidget.SongEntry entry = songListWidget.getSelectedOrNull();
if (entry != null) {
@ -106,9 +107,11 @@ public class DiscJockeyScreen extends Screen {
}
}));
TextFieldWidget searchBar = new TextFieldWidget(textRenderer, width / 2 - 75, height - 31, 150, 20, new TranslatableText(Main.MOD_ID+".screen.search"));
TextFieldWidget searchBar = new TextFieldWidget(textRenderer, width / 2 - 75, height - 31, 150, 20, Text.translatable(Main.MOD_ID+".screen.search"));
searchBar.setChangedListener(query -> {
this.query = query.toLowerCase().replaceAll("\\s", "");
query = query.toLowerCase().replaceAll("\\s", "");
if (this.query.equals(query)) return;
this.query = query;
shouldFilter = true;
});
addDrawableChild(searchBar);
@ -130,7 +133,17 @@ public class DiscJockeyScreen extends Screen {
shouldFilter = false;
songListWidget.setScrollAmount(0);
songListWidget.children().clear();
for (Song song : SongLoader.SONGS) if (song.searchableFileName.contains(query) || song.searchableName.contains(query)) songListWidget.children().add(song.entry);
boolean empty = query.isEmpty();
int favoriteIndex = 0;
for (Song song : SongLoader.SONGS) {
if (empty || song.searchableFileName.contains(query) || song.searchableName.contains(query)) {
if (song.entry.favorite) {
songListWidget.children().add(favoriteIndex++, song.entry);
} else {
songListWidget.children().add(song.entry);
}
}
}
}
}
@ -138,9 +151,4 @@ public class DiscJockeyScreen extends Screen {
public boolean shouldPause() {
return false;
}
@Override
public void onClose() {
super.onClose();
}
}

View File

@ -1,5 +1,4 @@
{
"disc_jockey.screen.title": "Disc Jockey",
"disc_jockey.screen.select_song": "Select A Song",
"disc_jockey.screen.play": "Play",
"disc_jockey.screen.play.stop": "Stop Playing",
@ -11,5 +10,10 @@
"disc_jockey.player.invalid_note_blocks": "The Note Blocks near you are not in the correct configuration. Missing:",
"disc_jockey.player.invalid_game_mode": "You can't play in %s",
"disc_jockey.player.to_far": "You went to far away",
"disc_jockey.still_loading": "The songs are still loading"
"disc_jockey.still_loading": "The songs are still loading",
"disc_jockey.loading": "Reloading all songs",
"disc_jockey.loading_done": "All songs are loaded",
"disc_jockey.song_not_found": " Song '%s' does not exist",
"disc_jockey.not_playing": "Not playing any song",
"disc_jockey.stopped_playing": "Stopped playing '%s'"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 359 B