mirror of
https://github.com/opelly27/Sokobot.git
synced 2026-05-20 06:27:34 +00:00
Compare commits
46 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| aeac78c2ad | |||
| 0ea7842d56 | |||
| 595482169c | |||
| 5774782238 | |||
| 29c1502ab5 | |||
| f3d9d34d9f | |||
| 38bc58ac0b | |||
| 89ffd3b679 | |||
| 5137d33e95 | |||
| 718d2b6c15 | |||
| 971a9c3328 | |||
| 44c4a944a0 | |||
| f336fe364b | |||
| 7e6d12700a | |||
| 6c3a422295 | |||
| 5f09809a6c | |||
| 268a46f3f7 | |||
| 9974622ac2 | |||
| 901db219a8 | |||
| 2a7080914f | |||
| 4034e235b3 | |||
| 0225b2675a | |||
| c69de31d0a | |||
| 0186eb4bf1 | |||
| ffce576045 | |||
| 653b480f8c | |||
| ae4ff41683 | |||
| 77c219434c | |||
| 8b35f31995 | |||
| 72cf1e742c | |||
| 75ff975e58 | |||
| e69a4672f0 | |||
| 396016eeba | |||
| 7c2a25bbcf | |||
| f57b271bbf | |||
| ecf173568e | |||
| d1077ca9ab | |||
| f39d3a7ac8 | |||
| 9de5a2ae36 | |||
| 79a09651ab | |||
| cf5e579e9d | |||
| 9347174aeb | |||
| 584cf828d5 | |||
| c9522a4362 | |||
| 3d76bc15d4 | |||
| 89bd9a8f30 |
@@ -0,0 +1,43 @@
|
|||||||
|
# Sokobot
|
||||||
|
|
||||||
|
Sokobot is a Discord bot written with [JDA](https://github.com/DV8FromTheWorld/JDA) that lets you play [Sokoban](https://en.wikipedia.org/wiki/Sokoban), the classic box-pushing puzzle game.
|
||||||
|
|
||||||
|
## Screenshots
|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
|
## Features
|
||||||
|
### Infinite levels
|
||||||
|
The maps in Sokobot are randomly generated, increasing in difficulty as you progress.
|
||||||
|
### Varied controls
|
||||||
|
Sokobot has multiple control options to improve the player's experience, including reactions and wasd commands!
|
||||||
|
### Simultaneous games
|
||||||
|
Thanks to the power of Java HashMaps™️, multiple users can use the bot at the same time without interfering with one another.
|
||||||
|
### Custom prefixes ``New!``
|
||||||
|
To prevent Sokobot from conflicting with other bots, admins can choose any single-character prefix to preface Sokobot's commands.
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
### User
|
||||||
|
- ``!play`` can be used to start a game if you are not currently in one.
|
||||||
|
- ``!stop`` can be used to stop your active game at any time.
|
||||||
|
- ``!info`` provides some useful details about the bot and rules of the game.
|
||||||
|
### Admin ``New!``
|
||||||
|
- ``!prefix [character]`` can be used to change the prefix the bot responds to in the current server.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
### Public host ``New!``
|
||||||
|
Sokobot is available on top.gg and can be added to your server [in one click](https://top.gg/bot/713635251703906336/)!
|
||||||
|
### Self-hosting
|
||||||
|
Grab the [latest .jar](https://github.com/PolyMarsDev/Sokobot/releases) or [build it yourself](#compiling). Then, create a Discord Bot Application [here](https://discord.com/developers/applications/) and paste the bot token into ``token.txt``. Then, ensure the two files are in the same directory and run the .jar file.
|
||||||
|
Please note, this bot differs a bit from the public bot. For example, there is no voting rewards (custom emotes are always unlocked), no leaderboard, no progress saving, etc.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Compiling
|
||||||
|
|
||||||
|
Install [Java 8 JDK](http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html) and [Gradle](https://gradle.org/).
|
||||||
|
In the root folder of the project, execute ``gradlew shadowJar``.
|
||||||
|
The compiled .jar file will be located in ``build/libs``.
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change. Feel free to create a fork and use the code for any noncommercial purposes.
|
||||||
+2
-1
@@ -5,7 +5,7 @@ plugins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
group 'com.polymars'
|
group 'com.polymars'
|
||||||
version '1.0'
|
version '1.1'
|
||||||
|
|
||||||
mainClassName = "Bot"
|
mainClassName = "Bot"
|
||||||
sourceCompatibility = 1.8;
|
sourceCompatibility = 1.8;
|
||||||
@@ -17,4 +17,5 @@ repositories {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile 'net.dv8tion:JDA:4.1.1_162'
|
compile 'net.dv8tion:JDA:4.1.1_162'
|
||||||
|
compile 'com.vdurmont:emoji-java:5.1.1'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
import net.dv8tion.jda.api.AccountType;
|
|
||||||
import net.dv8tion.jda.api.JDABuilder;
|
|
||||||
import net.dv8tion.jda.api.OnlineStatus;
|
|
||||||
import net.dv8tion.jda.api.entities.Activity;
|
|
||||||
|
|
||||||
import javax.security.auth.login.LoginException;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Paths;
|
|
||||||
|
|
||||||
public class Bot {
|
|
||||||
static String prefix = "!";
|
|
||||||
|
|
||||||
public static void main(String[] args) throws LoginException, IOException {
|
|
||||||
JDABuilder builder = new JDABuilder(AccountType.BOT);
|
|
||||||
String token = new String(Files.readAllBytes(Paths.get("token.txt")));
|
|
||||||
builder.setToken(token);
|
|
||||||
builder.setStatus(OnlineStatus.ONLINE);
|
|
||||||
builder.setActivity(Activity.playing("!play to play Sokoban!"));
|
|
||||||
builder.addEventListeners(new Commands());
|
|
||||||
builder.build();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,105 +0,0 @@
|
|||||||
import net.dv8tion.jda.api.EmbedBuilder;
|
|
||||||
import net.dv8tion.jda.api.entities.MessageChannel;
|
|
||||||
import net.dv8tion.jda.api.entities.User;
|
|
||||||
import net.dv8tion.jda.api.events.message.guild.GuildMessageReceivedEvent;
|
|
||||||
import net.dv8tion.jda.api.events.message.guild.react.GuildMessageReactionAddEvent;
|
|
||||||
import net.dv8tion.jda.api.hooks.ListenerAdapter;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.HashMap;
|
|
||||||
|
|
||||||
public class Commands extends ListenerAdapter {
|
|
||||||
HashMap<User, Game> games = new HashMap<User, Game>();
|
|
||||||
ArrayList<String> commands = new ArrayList<String>(Arrays.asList("w", "a", "s", "d", "up", "left", "down", "right", "r", Bot.prefix + "play", Bot.prefix + "continue", Bot.prefix + "stop"));
|
|
||||||
public void onGuildMessageReceived(GuildMessageReceivedEvent event)
|
|
||||||
{
|
|
||||||
if (event.getAuthor().isBot() && event.getMessage().getEmbeds().get(0).getTitle().charAt(0) == 'L')
|
|
||||||
{
|
|
||||||
event.getMessage().addReaction("U+2B05").queue();
|
|
||||||
event.getMessage().addReaction("U+27A1").queue();
|
|
||||||
event.getMessage().addReaction("U+2B06").queue();
|
|
||||||
event.getMessage().addReaction("U+2B07").queue();
|
|
||||||
event.getMessage().addReaction("U+1F504").queue();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
String[] args = event.getMessage().getContentRaw().split("\\s+");
|
|
||||||
|
|
||||||
if (args[0].toLowerCase().equals(Bot.prefix + "info"))
|
|
||||||
{
|
|
||||||
event.getChannel().sendMessage(info().build()).queue();
|
|
||||||
}
|
|
||||||
else if (commands.contains(args[0].toLowerCase()))
|
|
||||||
{
|
|
||||||
if (!games.containsKey(event.getAuthor()))
|
|
||||||
{
|
|
||||||
games.put(event.getAuthor(), new Game());
|
|
||||||
}
|
|
||||||
String userInput = args[0].toLowerCase();
|
|
||||||
if (Character.toString(userInput.charAt(0)).equals(Bot.prefix))
|
|
||||||
{
|
|
||||||
userInput = userInput.substring(1, userInput.length());
|
|
||||||
}
|
|
||||||
games.get(event.getAuthor()).run(event.getChannel(), userInput);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public void onGuildMessageReactionAdd(GuildMessageReactionAddEvent event) {
|
|
||||||
if (event.getMember().getUser().isBot())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!games.containsKey(event.getMember().getUser()))
|
|
||||||
{
|
|
||||||
games.put(event.getMember().getUser(), new Game());
|
|
||||||
}
|
|
||||||
String userInput = "";
|
|
||||||
switch (event.getReactionEmote().toString())
|
|
||||||
{
|
|
||||||
case "RE:U+2b05":
|
|
||||||
userInput = "left";
|
|
||||||
break;
|
|
||||||
case "RE:U+27a1":
|
|
||||||
userInput = "right";
|
|
||||||
break;
|
|
||||||
case "RE:U+2b06":
|
|
||||||
userInput = "up";
|
|
||||||
break;
|
|
||||||
case "RE:U+2b07":
|
|
||||||
userInput = "down";
|
|
||||||
break;
|
|
||||||
case "RE:U+1f504":
|
|
||||||
userInput = "r";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
games.get(event.getMember().getUser()).run(event.getChannel(), userInput);
|
|
||||||
}
|
|
||||||
EmbedBuilder info()
|
|
||||||
{
|
|
||||||
EmbedBuilder info = new EmbedBuilder();
|
|
||||||
info.setTitle("Sokobot");
|
|
||||||
info.setThumbnail("https://cdn.discordapp.com/avatars/713635251703906336/4094ba90942077c27549cccbd54cecd4.png?size=128");
|
|
||||||
info.setDescription("Sokobot is a bot that lets you play Sokoban, the classic box-pushing puzzle game.");
|
|
||||||
info.setColor(0xdd2e53);
|
|
||||||
info.addField("How to Play", "You are a **Sokoban** :flushed:.\nYour job is to push **boxes** :brown_square: on top of their **destinations** :negative_squared_cross_mark:.", false);
|
|
||||||
info.addField("Features", ":white_small_square:**Infinite levels**\nThe fun never ends thanks to endless, randomly generated maps of increasing difficulty!\n:white_small_square:**Varied controls**\nSokobot has multiple control options to improve the player's experience, including reactions and wasd commands!\n:white_small_square:**Simultaneous games**\nThanks to the power of Java HashMaps:tm:, multiple users can use the bot at the same time without interfering with eachothers' games.", false);
|
|
||||||
info.addField("Commands", ("``" + Bot.prefix + "play`` can be used to start a game if you are not currently in one.\n``" + Bot.prefix + "stop`` can be used to stop your active game at any time.\n``" + Bot.prefix + "info`` provides some useful details about the bot."), false);
|
|
||||||
info.addField("Source code", "https://github.com/polymarsdev", false);
|
|
||||||
info.setFooter("created by PolyMars", "https://avatars0.githubusercontent.com/u/51007356?s=460&u=4eb8fd498421a2eee9781edfbadf654386cf06c7&v=4");
|
|
||||||
return info;
|
|
||||||
}
|
|
||||||
public static void sendGameEmbed(MessageChannel channel, String level, String game)
|
|
||||||
{
|
|
||||||
EmbedBuilder embed = new EmbedBuilder();
|
|
||||||
embed.setTitle("Level " + level);
|
|
||||||
embed.setDescription(game);
|
|
||||||
embed.setFooter("Enter direction (up, down, left, right, or wasd) or r to reset");
|
|
||||||
channel.sendMessage(embed.build()).queue();
|
|
||||||
}
|
|
||||||
public static void sendWinEmbed(MessageChannel channel, String level)
|
|
||||||
{
|
|
||||||
EmbedBuilder embed = new EmbedBuilder();
|
|
||||||
embed.setTitle("You win!");
|
|
||||||
embed.setDescription("Type ``" + Bot.prefix + "continue`` to continue to Level " + level + " or ``" + Bot.prefix + "stop`` to quit ");
|
|
||||||
channel.sendMessage(embed.build()).queue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
import net.dv8tion.jda.api.entities.MessageChannel;
|
|
||||||
|
|
||||||
public class Game {
|
|
||||||
boolean gameActive = false;
|
|
||||||
public int level = 1;
|
|
||||||
int width = 9;
|
|
||||||
int height = 6;
|
|
||||||
Grid grid = new Grid(width, height, level);
|
|
||||||
public void newGame(MessageChannel channel)
|
|
||||||
{
|
|
||||||
if (!gameActive)
|
|
||||||
{
|
|
||||||
level = 1;
|
|
||||||
width = 9;
|
|
||||||
height = 6;
|
|
||||||
grid = new Grid(width, height, level);
|
|
||||||
|
|
||||||
gameActive = true;
|
|
||||||
Commands.sendGameEmbed(channel, String.valueOf(level), grid.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public void run(MessageChannel channel, String userInput)
|
|
||||||
{
|
|
||||||
if (userInput.equals("stop") && gameActive)
|
|
||||||
{
|
|
||||||
channel.sendMessage("Thanks for playing!").queue();
|
|
||||||
gameActive = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (userInput.equals("play") && !gameActive)
|
|
||||||
{
|
|
||||||
newGame(channel);
|
|
||||||
}
|
|
||||||
else if (gameActive)
|
|
||||||
{
|
|
||||||
if (!grid.hasWon()) {
|
|
||||||
|
|
||||||
String direction = userInput;
|
|
||||||
if (direction.equals("up") || direction.equals("w"))
|
|
||||||
{
|
|
||||||
grid.getPlayer().moveUp();
|
|
||||||
} else if (direction.equals("down") || direction.equals("s"))
|
|
||||||
{
|
|
||||||
grid.getPlayer().moveDown();
|
|
||||||
} else if (direction.equals("left") || direction.equals("a"))
|
|
||||||
{
|
|
||||||
grid.getPlayer().moveLeft();
|
|
||||||
} else if (direction.equals("right") || direction.equals("d"))
|
|
||||||
{
|
|
||||||
grid.getPlayer().moveRight();
|
|
||||||
} else if (direction.equals("r"))
|
|
||||||
{
|
|
||||||
grid.reset();
|
|
||||||
}
|
|
||||||
if (!grid.hasWon()) { //need to check again
|
|
||||||
Commands.sendGameEmbed(channel, String.valueOf(level), grid.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (grid.hasWon()) {
|
|
||||||
level += 1;
|
|
||||||
if (width < 13) {
|
|
||||||
width += 2;
|
|
||||||
}
|
|
||||||
if (height < 8)
|
|
||||||
{
|
|
||||||
height += 1;
|
|
||||||
}
|
|
||||||
Commands.sendWinEmbed(channel, String.valueOf(level));
|
|
||||||
grid = new Grid(width, height, level);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,214 +0,0 @@
|
|||||||
public class Grid
|
|
||||||
{
|
|
||||||
final int GROUND = 0;
|
|
||||||
final int WALL = 1;
|
|
||||||
final int BOX = 2;
|
|
||||||
final int DESTINATION = 3;
|
|
||||||
final int PLAYER = 4;
|
|
||||||
final int MAX_BOXES = 8;
|
|
||||||
Tile[][] grid;
|
|
||||||
Box[] boxes;
|
|
||||||
Destination[] destinations;
|
|
||||||
int boxCount;
|
|
||||||
int height = 0;
|
|
||||||
int width = 0;
|
|
||||||
int color = 0;
|
|
||||||
Player player;
|
|
||||||
Randomizer rand = new Randomizer();
|
|
||||||
public Grid(int width, int height, int boxCount) //create a random grid with specific width, height, and number of boxes
|
|
||||||
{
|
|
||||||
player = new Player(2, 2, this);
|
|
||||||
if (boxCount > MAX_BOXES)
|
|
||||||
{
|
|
||||||
boxCount = MAX_BOXES;
|
|
||||||
}
|
|
||||||
this.boxCount = boxCount;
|
|
||||||
boxes = new Box[boxCount];
|
|
||||||
destinations = new Destination[boxCount];
|
|
||||||
this.height = height;
|
|
||||||
this.width = width;
|
|
||||||
grid = new Tile[width][height];
|
|
||||||
createBoxes();
|
|
||||||
createDestinations();
|
|
||||||
updateGrid();
|
|
||||||
}
|
|
||||||
public Player getPlayer()
|
|
||||||
{
|
|
||||||
return player;
|
|
||||||
}
|
|
||||||
public void reset()
|
|
||||||
{
|
|
||||||
player.setPosition(2, 2);
|
|
||||||
for (int i = 0; i < boxCount; i++)
|
|
||||||
{
|
|
||||||
boxes[i].reset();
|
|
||||||
}
|
|
||||||
updateGrid();
|
|
||||||
}
|
|
||||||
public void setStatus(int x, int y, int status)
|
|
||||||
{
|
|
||||||
grid[x][y].setStatus(status);
|
|
||||||
}
|
|
||||||
public int getStatus(int x, int y)
|
|
||||||
{
|
|
||||||
return grid[x][y].getStatus();
|
|
||||||
}
|
|
||||||
public boolean isWall(int x, int y)
|
|
||||||
{
|
|
||||||
return grid[x][y].getStatus() == WALL;
|
|
||||||
}
|
|
||||||
public boolean isBox(int x, int y)
|
|
||||||
{
|
|
||||||
return grid[x][y].getStatus() == BOX;
|
|
||||||
}
|
|
||||||
public boolean isBoxRaw(int x, int y) //allows you to check if a box is at a position before grid is set up
|
|
||||||
{
|
|
||||||
for (int i = 0; i < boxCount; i++)
|
|
||||||
{
|
|
||||||
if (x == boxes[i].getX() && y == boxes[i].getY())
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
public boolean isDestination(int x, int y)
|
|
||||||
{
|
|
||||||
return grid[x][y].getStatus() == DESTINATION;
|
|
||||||
|
|
||||||
}
|
|
||||||
public Box getBox(int x, int y)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < boxCount; i++)
|
|
||||||
{
|
|
||||||
if (x == boxes[i].getX() && y == boxes[i].getY())
|
|
||||||
{
|
|
||||||
return boxes[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
public void createBoxes()
|
|
||||||
{
|
|
||||||
color = rand.nextInt(6); //runs after each level
|
|
||||||
for (int i = 0; i < boxCount; i++)
|
|
||||||
{
|
|
||||||
int x = rand.nextInt(width - 4) + 2;
|
|
||||||
int y = rand.nextInt(height - 4) + 2;
|
|
||||||
for (int j = 0; j < i; j++)
|
|
||||||
{
|
|
||||||
while ((x == boxes[j].getX() && y == boxes[j].getY()) || (x == 2 && y == 2))
|
|
||||||
{
|
|
||||||
x = rand.nextInt(width - 4) + 2;
|
|
||||||
y = rand.nextInt(height - 4) + 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
boxes[i] = new Box(x, y, this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public void createDestinations()
|
|
||||||
{
|
|
||||||
for (int i = 0; i < boxCount; i++)
|
|
||||||
{
|
|
||||||
int x = rand.nextInt(width - 2) + 1;
|
|
||||||
int y = rand.nextInt(height - 2) + 1;
|
|
||||||
for (int j = 0; j < i; j++)
|
|
||||||
{
|
|
||||||
while (((x == destinations[j].getX() && y == destinations[j].getY())) || isBoxRaw(x, y))
|
|
||||||
{
|
|
||||||
x = rand.nextInt(width - 2) + 1;
|
|
||||||
y = rand.nextInt(height - 2) + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
destinations[i] = new Destination(x, y, this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public void updateGrid()
|
|
||||||
{
|
|
||||||
for (int i = 0; i < height; i++)
|
|
||||||
{
|
|
||||||
for (int j = 0; j < width; j++)
|
|
||||||
{
|
|
||||||
grid[j][i] = new Tile(GROUND);
|
|
||||||
if (j == 0 || j == width - 1 || i == 0 || i == height - 1)
|
|
||||||
{
|
|
||||||
grid[j][i] = new Tile(WALL, color);
|
|
||||||
}
|
|
||||||
for (int k = 0; k < boxCount; k++)
|
|
||||||
{
|
|
||||||
if (destinations[k].getX() == j && destinations[k].getY() == i)
|
|
||||||
{
|
|
||||||
grid[j][i] = new Tile(DESTINATION);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (player.getX() == j && player.getY() == i)
|
|
||||||
{
|
|
||||||
grid[j][i] = new Tile(PLAYER);
|
|
||||||
}
|
|
||||||
for (int k = 0; k < boxCount; k++)
|
|
||||||
{
|
|
||||||
if (boxes[k].getX() == j && boxes[k].getY() == i)
|
|
||||||
{
|
|
||||||
if (boxes[k].onDestination())
|
|
||||||
{
|
|
||||||
grid[j][i] = new Tile(WALL, color);
|
|
||||||
} else {
|
|
||||||
grid[j][i] = new Tile(BOX);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public boolean hasWon()
|
|
||||||
{
|
|
||||||
for (int i = 0; i < boxCount; i++)
|
|
||||||
{
|
|
||||||
if (!destinations[i].hasBox(this))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
public Tile[][] getGrid()
|
|
||||||
{
|
|
||||||
return grid;
|
|
||||||
}
|
|
||||||
public Box[] getBoxes()
|
|
||||||
{
|
|
||||||
return boxes;
|
|
||||||
}
|
|
||||||
public Destination[] getDestinations()
|
|
||||||
{
|
|
||||||
return destinations;
|
|
||||||
}
|
|
||||||
public int getBoxCount()
|
|
||||||
{
|
|
||||||
return boxCount;
|
|
||||||
}
|
|
||||||
public int getHeight()
|
|
||||||
{
|
|
||||||
return height;
|
|
||||||
}
|
|
||||||
public int getWidth()
|
|
||||||
{
|
|
||||||
return width;
|
|
||||||
}
|
|
||||||
public String toString()
|
|
||||||
{
|
|
||||||
updateGrid();
|
|
||||||
String result = "";
|
|
||||||
for (int i = 0; i < height; i++)
|
|
||||||
{
|
|
||||||
for (int j = 0; j < width; j++)
|
|
||||||
{
|
|
||||||
result += grid[j][i];
|
|
||||||
}
|
|
||||||
result += "\n";
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,98 +0,0 @@
|
|||||||
public class Player
|
|
||||||
{
|
|
||||||
int x = 0;
|
|
||||||
int y = 0;
|
|
||||||
Grid currentGrid;
|
|
||||||
public Player(int x, int y, Grid currentGrid)
|
|
||||||
{
|
|
||||||
this.x = x;
|
|
||||||
this.y = y;
|
|
||||||
this.currentGrid = currentGrid;
|
|
||||||
}
|
|
||||||
public int getX()
|
|
||||||
{
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
public int getY()
|
|
||||||
{
|
|
||||||
return y;
|
|
||||||
}
|
|
||||||
public void setPosition(int x, int y)
|
|
||||||
{
|
|
||||||
this.x = x;
|
|
||||||
this.y = y;
|
|
||||||
}
|
|
||||||
public boolean moveUp()
|
|
||||||
{
|
|
||||||
if (!currentGrid.isWall(x, y - 1))
|
|
||||||
{
|
|
||||||
if (currentGrid.isBox(x, y - 1))
|
|
||||||
{
|
|
||||||
if (currentGrid.getBox(x, y - 1).moveUp())
|
|
||||||
{
|
|
||||||
y -= 1;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
y -= 1;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
public boolean moveDown()
|
|
||||||
{
|
|
||||||
if (!currentGrid.isWall(x, y + 1))
|
|
||||||
{
|
|
||||||
if (currentGrid.isBox(x, y + 1))
|
|
||||||
{
|
|
||||||
if (currentGrid.getBox(x, y + 1).moveDown())
|
|
||||||
{
|
|
||||||
y += 1;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
y += 1;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
public boolean moveLeft()
|
|
||||||
{
|
|
||||||
if (!currentGrid.isWall(x - 1, y))
|
|
||||||
{
|
|
||||||
if (currentGrid.isBox(x - 1, y))
|
|
||||||
{
|
|
||||||
if (currentGrid.getBox(x - 1, y).moveLeft())
|
|
||||||
{
|
|
||||||
x -= 1;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
x -= 1;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
public boolean moveRight()
|
|
||||||
{
|
|
||||||
if (!currentGrid.isWall(x + 1, y))
|
|
||||||
{
|
|
||||||
if (currentGrid.isBox(x + 1, y))
|
|
||||||
{
|
|
||||||
if (currentGrid.getBox(x + 1, y).moveRight())
|
|
||||||
{
|
|
||||||
x += 1;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
x += 1;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,190 @@
|
|||||||
|
package me.polymarsdev.sokobot;
|
||||||
|
|
||||||
|
import me.polymarsdev.sokobot.database.Database;
|
||||||
|
import me.polymarsdev.sokobot.listener.CommandListener;
|
||||||
|
import me.polymarsdev.sokobot.listener.GameListener;
|
||||||
|
import me.polymarsdev.sokobot.util.GameUtil;
|
||||||
|
import net.dv8tion.jda.api.JDA;
|
||||||
|
import net.dv8tion.jda.api.OnlineStatus;
|
||||||
|
import net.dv8tion.jda.api.entities.Activity;
|
||||||
|
import net.dv8tion.jda.api.entities.Guild;
|
||||||
|
import net.dv8tion.jda.api.requests.GatewayIntent;
|
||||||
|
import net.dv8tion.jda.api.sharding.DefaultShardManagerBuilder;
|
||||||
|
import net.dv8tion.jda.api.sharding.ShardManager;
|
||||||
|
import net.dv8tion.jda.api.utils.cache.CacheFlag;
|
||||||
|
|
||||||
|
import javax.security.auth.login.LoginException;
|
||||||
|
import java.io.File;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public class Bot {
|
||||||
|
static HashMap<Long, String> prefixes = new HashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* You can enable the database here.
|
||||||
|
* Set the DB Type to MySQL or SQLite, which you want to use.
|
||||||
|
* -
|
||||||
|
* You can configure login data in the Database class.
|
||||||
|
*/
|
||||||
|
private static final boolean enableDatabase = false;
|
||||||
|
private static final Database.DBType dbType = Database.DBType.SQLite;
|
||||||
|
|
||||||
|
public static boolean debug = false;
|
||||||
|
|
||||||
|
private static ShardManager shardManager;
|
||||||
|
private static Database database = null;
|
||||||
|
|
||||||
|
public static void main(String[] args) throws LoginException {
|
||||||
|
String token = null;
|
||||||
|
try {
|
||||||
|
File tokenFile = Paths.get("token.txt").toFile();
|
||||||
|
if (!tokenFile.exists()) {
|
||||||
|
System.out.println("[ERROR] Could not find token.txt file");
|
||||||
|
System.out.print("Please paste in your bot token: ");
|
||||||
|
Scanner s = new Scanner(System.in);
|
||||||
|
token = s.nextLine();
|
||||||
|
System.out.println();
|
||||||
|
System.out.println("[INFO] Creating token.txt - please wait");
|
||||||
|
if (!tokenFile.createNewFile()) {
|
||||||
|
System.out.println(
|
||||||
|
"[ERROR] Could not create token.txt - please create this file and paste in your token"
|
||||||
|
+ ".");
|
||||||
|
s.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Files.write(tokenFile.toPath(), token.getBytes());
|
||||||
|
s.close();
|
||||||
|
}
|
||||||
|
token = new String(Files.readAllBytes(tokenFile.toPath()));
|
||||||
|
} catch (Exception ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
}
|
||||||
|
if (token == null) return;
|
||||||
|
if (enableDatabase) database = new Database(dbType);
|
||||||
|
if (database != null) {
|
||||||
|
if (!database.isConnected()) {
|
||||||
|
database = null;
|
||||||
|
System.out.println("[ERROR] Database connection failed. Continuing without database.");
|
||||||
|
} else {
|
||||||
|
database.update(
|
||||||
|
"CREATE TABLE IF NOT EXISTS guildprefix (guildId VARCHAR(18) NOT NULL, prefix VARCHAR(8) NOT "
|
||||||
|
+ "NULL);");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
List<GatewayIntent> intents = new ArrayList<>(
|
||||||
|
Arrays.asList(GatewayIntent.GUILD_MESSAGES, GatewayIntent.GUILD_EMOJIS,
|
||||||
|
GatewayIntent.GUILD_MESSAGE_REACTIONS));
|
||||||
|
DefaultShardManagerBuilder builder = DefaultShardManagerBuilder.create(token, intents);
|
||||||
|
builder.setStatus(OnlineStatus.ONLINE);
|
||||||
|
builder.setActivity(Activity.playing("@Sokobot for info!"));
|
||||||
|
builder.addEventListeners(new GameListener(), new CommandListener());
|
||||||
|
builder.disableCache(
|
||||||
|
CacheFlag.CLIENT_STATUS, CacheFlag.ACTIVITY, CacheFlag.MEMBER_OVERRIDES, CacheFlag.VOICE_STATE);
|
||||||
|
shardManager = builder.build();
|
||||||
|
GameUtil.runGameTimer();
|
||||||
|
Thread consoleThread = new Thread(() -> {
|
||||||
|
Scanner s = new Scanner(System.in);
|
||||||
|
while (s.hasNextLine()) {
|
||||||
|
processCommand(s.nextLine());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
consoleThread.setDaemon(true);
|
||||||
|
consoleThread.setName("Console Thread");
|
||||||
|
consoleThread.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void processCommand(String cmd) {
|
||||||
|
if (cmd.equalsIgnoreCase("help")) {
|
||||||
|
System.out.println("Commands:\nstop - Shuts down the bot and exits the program\ndebug - Toggle debug mode");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (cmd.equalsIgnoreCase("debug")) {
|
||||||
|
debug = !debug;
|
||||||
|
String response = debug ? "on" : "off";
|
||||||
|
System.out.println("[INFO] Turned " + response + " debug mode");
|
||||||
|
Bot.debug("Make sure to turn off debug mode after necessary information has been collected.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (cmd.equalsIgnoreCase("stop")) {
|
||||||
|
System.out.println("Shutting down...");
|
||||||
|
shardManager.shutdown();
|
||||||
|
if (database != null) {
|
||||||
|
System.out.println("Disconnecting database...");
|
||||||
|
database.disconnect();
|
||||||
|
}
|
||||||
|
System.out.println("Bye!");
|
||||||
|
System.exit(0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
System.out.println("Unknown command. Please use \"help\" for a list of commands.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Debug Info for Developer information
|
||||||
|
> Limit update to 10 seconds minimum because of JDA shard checks
|
||||||
|
*/
|
||||||
|
private static long lastDebugInfoUpdate = -1L;
|
||||||
|
private static String debugInfo = "";
|
||||||
|
|
||||||
|
private static void updateDebugInfo() {
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
if (now - lastDebugInfoUpdate < 10000) return;
|
||||||
|
lastDebugInfoUpdate = now;
|
||||||
|
int a = enableDatabase ? 1 : 0;
|
||||||
|
int b = enableDatabase ? database.isConnected() ? 1 : 0 : 0;
|
||||||
|
int c = 0;
|
||||||
|
int d = shardManager.getShardsTotal();
|
||||||
|
for (JDA shard : shardManager.getShards()) if (shard.getStatus() == JDA.Status.CONNECTED) c++;
|
||||||
|
debugInfo = a + b + c + d + "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print a message when debug is on
|
||||||
|
public static void debug(String log) {
|
||||||
|
if (debug) {
|
||||||
|
updateDebugInfo();
|
||||||
|
System.out.println("[DEBUG " + debugInfo + "] " + log);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ShardManager getShardManager() {
|
||||||
|
return shardManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void removePrefix(long guildId) {
|
||||||
|
prefixes.remove(guildId);
|
||||||
|
if (database != null) {
|
||||||
|
database.update("DELETE FROM guildprefix WHERE guildId=?;", String.valueOf(guildId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setPrefix(Guild guild, String prefix) {
|
||||||
|
prefixes.put(guild.getIdLong(), prefix);
|
||||||
|
if (database != null) {
|
||||||
|
database.update("DELETE FROM guildprefix WHERE guildId=?;", guild.getId());
|
||||||
|
database.update("INSERT INTO guildprefix VALUES (?, ?);", guild.getId(), prefix);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getPrefix(Guild guild) {
|
||||||
|
if (prefixes.containsKey(guild.getIdLong())) return prefixes.get(guild.getIdLong());
|
||||||
|
if (database != null) {
|
||||||
|
try (ResultSet rs = database.query("SELECT prefix FROM guildprefix WHERE guildId=?;", guild.getId())) {
|
||||||
|
if (rs.next()) {
|
||||||
|
String prefix = rs.getString("prefix");
|
||||||
|
prefixes.put(guild.getIdLong(), prefix);
|
||||||
|
return prefix;
|
||||||
|
}
|
||||||
|
prefixes.put(guild.getIdLong(), "!");
|
||||||
|
return "!";
|
||||||
|
} catch (SQLException ex) {
|
||||||
|
System.out.println("[ERROR] Error at retrieving guild prefix of guild id " + guild.getId() + ": " + ex
|
||||||
|
.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "!";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,134 @@
|
|||||||
|
package me.polymarsdev.sokobot;
|
||||||
|
|
||||||
|
import me.polymarsdev.sokobot.objects.Grid;
|
||||||
|
import me.polymarsdev.sokobot.util.GameUtil;
|
||||||
|
import net.dv8tion.jda.api.entities.*;
|
||||||
|
import net.dv8tion.jda.api.requests.RestAction;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
public class Game {
|
||||||
|
long gameMessageID;
|
||||||
|
long channelID;
|
||||||
|
User user;
|
||||||
|
String playerEmote = ":flushed:";
|
||||||
|
public boolean gameActive = false;
|
||||||
|
public int level = 1;
|
||||||
|
int width = 9;
|
||||||
|
int height = 6;
|
||||||
|
public long lastAction;
|
||||||
|
Grid grid = new Grid(width, height, level, playerEmote);
|
||||||
|
|
||||||
|
public Game(User user) {
|
||||||
|
this.user = user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPlayerEmote(String emote) {
|
||||||
|
playerEmote = emote;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setGameMessage(Message gameMessage) {
|
||||||
|
// To avoid an Unknown Message error, we will store the IDs and retrieve the Channel object when needed.
|
||||||
|
gameMessageID = gameMessage.getIdLong();
|
||||||
|
channelID = gameMessage.getChannel().getIdLong();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void newGame(MessageChannel channel) {
|
||||||
|
if (!gameActive) {
|
||||||
|
width = 9;
|
||||||
|
height = 6;
|
||||||
|
for (int i = 1; i < level; i++) updateWidthHeight();
|
||||||
|
grid = new Grid(width, height, level, playerEmote);
|
||||||
|
gameActive = true;
|
||||||
|
lastAction = System.currentTimeMillis();
|
||||||
|
GameUtil.sendGameEmbed(channel, String.valueOf(level), grid.toString(), user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This method used to something earlier. (I actually just forgot what I used it for)
|
||||||
|
// It did not work like it was supposed to, so it was changed to this basic line.
|
||||||
|
private void queue(RestAction<Message> restAction, Consumer<? super Message> success) {
|
||||||
|
restAction.queue(success);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stop() {
|
||||||
|
gameActive = false;
|
||||||
|
TextChannel textChannel = Bot.getShardManager().getTextChannelById(channelID);
|
||||||
|
if (textChannel != null) {
|
||||||
|
textChannel.retrieveMessageById(gameMessageID).queue(gameMessage -> gameMessage.delete().queue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void run(Guild guild, TextChannel channel, String userInput) {
|
||||||
|
if (userInput.equals("stop") && gameActive) {
|
||||||
|
stop();
|
||||||
|
channel.sendMessage("Thanks for playing, " + user.getAsMention() + "!")
|
||||||
|
.queue(msg -> msg.delete().queueAfter(10, TimeUnit.SECONDS));
|
||||||
|
}
|
||||||
|
if (userInput.equals("play") && !gameActive) {
|
||||||
|
newGame(channel);
|
||||||
|
} else if (gameActive) {
|
||||||
|
lastAction = System.currentTimeMillis();
|
||||||
|
boolean won = grid.hasWon();
|
||||||
|
if (!won) {
|
||||||
|
boolean moved = false;
|
||||||
|
switch (userInput) {
|
||||||
|
case "up":
|
||||||
|
case "w":
|
||||||
|
moved = grid.getPlayer().moveUp();
|
||||||
|
break;
|
||||||
|
case "down":
|
||||||
|
case "s":
|
||||||
|
moved = grid.getPlayer().moveDown();
|
||||||
|
break;
|
||||||
|
case "left":
|
||||||
|
case "a":
|
||||||
|
moved = grid.getPlayer().moveLeft();
|
||||||
|
break;
|
||||||
|
case "right":
|
||||||
|
case "d":
|
||||||
|
moved = grid.getPlayer().moveRight();
|
||||||
|
break;
|
||||||
|
case "mr":
|
||||||
|
grid.resetMap();
|
||||||
|
moved = true;
|
||||||
|
break;
|
||||||
|
case "r":
|
||||||
|
grid.reset();
|
||||||
|
moved = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
grid.updateGrid();
|
||||||
|
won = grid.hasWon();
|
||||||
|
if (!won && moved) {
|
||||||
|
TextChannel textChannel = Bot.getShardManager().getTextChannelById(channelID);
|
||||||
|
if (textChannel != null) {
|
||||||
|
queue(textChannel.retrieveMessageById(gameMessageID), gameMessage -> GameUtil
|
||||||
|
.updateGameEmbed(gameMessage, String.valueOf(level), grid.toString(), user));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (won) {
|
||||||
|
level += 1;
|
||||||
|
updateWidthHeight();
|
||||||
|
TextChannel textChannel = Bot.getShardManager().getTextChannelById(channelID);
|
||||||
|
if (textChannel != null) {
|
||||||
|
queue(
|
||||||
|
textChannel.retrieveMessageById(gameMessageID),
|
||||||
|
gameMessage -> GameUtil.sendWinEmbed(guild, gameMessage, String.valueOf(level)));
|
||||||
|
}
|
||||||
|
grid = new Grid(width, height, level, playerEmote);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateWidthHeight() {
|
||||||
|
if (width < 13) {
|
||||||
|
width += 2;
|
||||||
|
}
|
||||||
|
if (height < 8) {
|
||||||
|
height += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
package me.polymarsdev.sokobot.commands;
|
||||||
|
|
||||||
|
import com.vdurmont.emoji.EmojiManager;
|
||||||
|
import me.polymarsdev.sokobot.Bot;
|
||||||
|
import me.polymarsdev.sokobot.Game;
|
||||||
|
import me.polymarsdev.sokobot.entity.Command;
|
||||||
|
import me.polymarsdev.sokobot.event.CommandEvent;
|
||||||
|
import me.polymarsdev.sokobot.util.GameUtil;
|
||||||
|
import net.dv8tion.jda.api.Permission;
|
||||||
|
import net.dv8tion.jda.api.entities.Guild;
|
||||||
|
import net.dv8tion.jda.api.entities.TextChannel;
|
||||||
|
import net.dv8tion.jda.api.entities.User;
|
||||||
|
|
||||||
|
public class GameInputCommand extends Command {
|
||||||
|
|
||||||
|
public GameInputCommand(String name) {
|
||||||
|
super(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(CommandEvent event) {
|
||||||
|
User user = event.getAuthor();
|
||||||
|
String[] args = event.getArgs();
|
||||||
|
String prefix = Bot.getPrefix(event.getGuild());
|
||||||
|
Game game;
|
||||||
|
if (!GameUtil.hasGame(user.getIdLong())) {
|
||||||
|
game = new Game(user);
|
||||||
|
GameUtil.setGame(user.getIdLong(), game);
|
||||||
|
} else game = GameUtil.getGame(user.getIdLong());
|
||||||
|
//
|
||||||
|
String userInput = this.getName().toLowerCase();
|
||||||
|
Bot.debug("Processing game input: " + userInput);
|
||||||
|
if (userInput.equals("play")) {
|
||||||
|
if (!game.gameActive) {
|
||||||
|
if (args.length > 0 && EmojiManager.isEmoji(args[0])) game.setPlayerEmote(args[0]);
|
||||||
|
} else {
|
||||||
|
event.reply(user.getAsMention() + ", you already have an active game.\nUse `" + prefix
|
||||||
|
+ "stop` to stop your current game first.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Guild guild = event.getGuild();
|
||||||
|
TextChannel channel = event.getTextChannel();
|
||||||
|
game.run(event.getGuild(), channel, userInput);
|
||||||
|
if (userInput.equals("stop")) GameUtil.removeGame(user.getIdLong());
|
||||||
|
if (game.gameActive && guild.getSelfMember().hasPermission(channel, Permission.MESSAGE_MANAGE))
|
||||||
|
event.getMessage().delete().queue();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
package me.polymarsdev.sokobot.commands;
|
||||||
|
|
||||||
|
import me.polymarsdev.sokobot.Bot;
|
||||||
|
import me.polymarsdev.sokobot.entity.Command;
|
||||||
|
import me.polymarsdev.sokobot.event.CommandEvent;
|
||||||
|
import net.dv8tion.jda.api.EmbedBuilder;
|
||||||
|
import net.dv8tion.jda.api.entities.Guild;
|
||||||
|
|
||||||
|
public class InfoCommand extends Command {
|
||||||
|
|
||||||
|
public InfoCommand() {
|
||||||
|
super("info");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(CommandEvent event) {
|
||||||
|
Bot.debug("Received info command (or bot mention)");
|
||||||
|
Guild guild = event.getGuild();
|
||||||
|
EmbedBuilder info = new EmbedBuilder();
|
||||||
|
final String prefix = Bot.getPrefix(guild);
|
||||||
|
info.setTitle("Sokobot");
|
||||||
|
info.setThumbnail(guild.getSelfMember().getUser().getAvatarUrl());
|
||||||
|
info.setDescription("Sokobot is a bot that lets you play Sokoban, the classic box-pushing puzzle game.");
|
||||||
|
info.setColor(0xdd2e53);
|
||||||
|
info.addField("How to Play", "You are a **Sokoban** :flushed:.\nYour job is to push **boxes** :brown_square: "
|
||||||
|
+ "on top of their **destinations** :negative_squared_cross_mark:.", false);
|
||||||
|
info.addField("Features", ":white_small_square:**Infinite levels**\nThe maps in Sokobot are randomly "
|
||||||
|
+ "generated, increasing in difficulty as you progress.\n:white_small_square:**Varied " + "controls"
|
||||||
|
+ "**\nSokobot has multiple control options to improve the player's experience, including "
|
||||||
|
+ "reactions and wasd commands!\n:white_small_square:**Simultaneous games**\nThanks to the power of "
|
||||||
|
+ "Java HashMaps:tm:, multiple users can use the bot at the same time without interfering with one "
|
||||||
|
+ "another.\n:white_small_square:**Custom prefixes**\nTo prevent Sokobot from conflicting with other "
|
||||||
|
+ "bots, admins can choose any single-character prefix to preface Sokobot's commands.", false);
|
||||||
|
info.addField(
|
||||||
|
"Commands",
|
||||||
|
("``" + prefix + "play`` can be used to start a game if you are not " + "currently in " + "one.\n``"
|
||||||
|
+ prefix + "stop`` can be used to stop your active game at any " + "time.\n``" + prefix
|
||||||
|
+ "info`` provides some useful details about the bot and " + "rules of " + "the game.\n``" + Bot
|
||||||
|
.getPrefix(guild) + "prefix [character]`` can be used to " + "change the prefix the "
|
||||||
|
+ "bot responds to."), false);
|
||||||
|
info.addField(
|
||||||
|
"Add to your server",
|
||||||
|
"https://top.gg/bot/713635251703906336\nSokobot is currently in " + Bot.getShardManager().getGuilds()
|
||||||
|
.size() + " servers.", false);
|
||||||
|
/*
|
||||||
|
// Official Support Server
|
||||||
|
info.addField("Support / Feedback",
|
||||||
|
"Official Support Server: https://invite.affluentproductions.org/apserver", false);
|
||||||
|
*/
|
||||||
|
info.addField("Source code", "https://github.com/PolyMarsDev/Sokobot", false);
|
||||||
|
info.setFooter("created by PolyMars", "https://avatars0.githubusercontent" + ".com/u/51007356?s=460&u"
|
||||||
|
+ "=4eb8fd498421a2eee9781edfbadf654386cf06c7&v=4");
|
||||||
|
event.reply(info.build());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package me.polymarsdev.sokobot.commands;
|
||||||
|
|
||||||
|
import me.polymarsdev.sokobot.Bot;
|
||||||
|
import me.polymarsdev.sokobot.entity.Command;
|
||||||
|
import me.polymarsdev.sokobot.event.CommandEvent;
|
||||||
|
import net.dv8tion.jda.api.Permission;
|
||||||
|
import net.dv8tion.jda.api.entities.Guild;
|
||||||
|
import net.dv8tion.jda.api.entities.Member;
|
||||||
|
import net.dv8tion.jda.api.entities.User;
|
||||||
|
|
||||||
|
public class PrefixCommand extends Command {
|
||||||
|
|
||||||
|
public PrefixCommand() {
|
||||||
|
super("prefix");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(CommandEvent event) {
|
||||||
|
User user = event.getAuthor();
|
||||||
|
Member member = event.getMember();
|
||||||
|
String[] args = event.getArgs();
|
||||||
|
Guild guild = event.getGuild();
|
||||||
|
Bot.debug("Received prefix command: " + event.getMessage().getContentRaw());
|
||||||
|
if (args.length > 0) {
|
||||||
|
if (!member.hasPermission(Permission.ADMINISTRATOR)) {
|
||||||
|
Bot.debug("Failed to change prefix of " + guild.getName() + " (" + guild.getId()
|
||||||
|
+ "): Insufficient permissions");
|
||||||
|
event.reply(user.getAsMention() + ", you do not have permission to use this command.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String newPrefix = args[0].toLowerCase();
|
||||||
|
if (newPrefix.length() > 1) {
|
||||||
|
Bot.debug("Failed to change prefix of " + guild.getName() + " (" + guild.getId() + "): length");
|
||||||
|
event.reply(user.getAsMention() + ", the prefix must be one character long!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Bot.setPrefix(guild, newPrefix);
|
||||||
|
Bot.debug("Successfully changed server prefix of " + guild.getName() + " (" + guild.getId() + ") to: "
|
||||||
|
+ newPrefix);
|
||||||
|
event.reply("Prefix successfully changed to ``" + newPrefix + "``.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
event.reply(user.getAsMention() + ", please use `" + Bot.getPrefix(guild)
|
||||||
|
+ "prefix <new prefix>` to set a server-prefix.");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,117 @@
|
|||||||
|
package me.polymarsdev.sokobot.database;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.sql.*;
|
||||||
|
|
||||||
|
public class Database {
|
||||||
|
|
||||||
|
public enum DBType {MySQL, SQLite}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SQLite Data
|
||||||
|
* Set this data if you use DBType#SQLite
|
||||||
|
* <p>
|
||||||
|
* field filePath - This can either be a relative or absolute path.
|
||||||
|
* ex: sokobot.db
|
||||||
|
* or: C:/sqlite/db/sokobot.db
|
||||||
|
*/
|
||||||
|
private final String filePath = "sokobot.db";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MySQL Data
|
||||||
|
* Set this data if you use DBType#MySQL
|
||||||
|
*/
|
||||||
|
private final String mysql_hostname = "127.0.0.1";
|
||||||
|
private final int mysql_port = 3306;
|
||||||
|
private final String mysql_database = "sokobot";
|
||||||
|
private final String mysql_username = "sokobot";
|
||||||
|
private final String mysql_password = "$€cUR€_P4sSw0R!)";
|
||||||
|
|
||||||
|
private Connection con = null;
|
||||||
|
|
||||||
|
public Database(DBType dbType) {
|
||||||
|
try {
|
||||||
|
if (dbType == DBType.MySQL) {
|
||||||
|
con = DriverManager.getConnection(
|
||||||
|
"jdbc:mysql://" + mysql_hostname + ":" + mysql_port + "/" + mysql_database
|
||||||
|
+ "?autoReconnect=true", mysql_username, mysql_password);
|
||||||
|
System.out.println("[INFO] Successfully initialized database connection.");
|
||||||
|
} else if (dbType == DBType.SQLite) {
|
||||||
|
File sqliteFile = new File(filePath);
|
||||||
|
if (!sqliteFile.exists()) {
|
||||||
|
System.out.println("[INFO] SQLite file \"" + filePath + "\" not found, creating file...");
|
||||||
|
boolean create = sqliteFile.createNewFile();
|
||||||
|
if (!create) System.out.println("[ERROR] Could not create SQLite file at " + filePath);
|
||||||
|
}
|
||||||
|
con = DriverManager.getConnection("jdbc:sqlite:" + filePath);
|
||||||
|
System.out.println("[INFO] Successfully initialized database connection.");
|
||||||
|
}
|
||||||
|
} catch (Exception ex) {
|
||||||
|
System.out.println("[ERROR] Error at creating database connection: " + ex.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void disconnect() {
|
||||||
|
try {
|
||||||
|
con.clearWarnings();
|
||||||
|
con.close();
|
||||||
|
con = null;
|
||||||
|
} catch (SQLException ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Connection getCon() {
|
||||||
|
return con;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResultSet query(String sql, Object... preparedParameters) {
|
||||||
|
try {
|
||||||
|
PreparedStatement ps = con.prepareStatement(sql);
|
||||||
|
int id = 1;
|
||||||
|
for (Object preparedParameter : preparedParameters) {
|
||||||
|
ps.setObject(id, preparedParameter);
|
||||||
|
id++;
|
||||||
|
}
|
||||||
|
return ps.executeQuery();
|
||||||
|
} catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResultSet query(String sql) {
|
||||||
|
try {
|
||||||
|
return con.prepareStatement(sql).executeQuery();
|
||||||
|
} catch (SQLException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void update(String sql, Object... preparedParameters) {
|
||||||
|
try (PreparedStatement ps = con.prepareStatement(sql)) {
|
||||||
|
int id = 1;
|
||||||
|
for (Object preparedParameter : preparedParameters) {
|
||||||
|
ps.setObject(id, preparedParameter);
|
||||||
|
id++;
|
||||||
|
}
|
||||||
|
ps.executeUpdate();
|
||||||
|
} catch (SQLException ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void update(String sql) {
|
||||||
|
try (PreparedStatement ps = con.prepareStatement(sql)) {
|
||||||
|
ps.executeUpdate();
|
||||||
|
} catch (SQLException ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isConnected() {
|
||||||
|
return con != null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package me.polymarsdev.sokobot.entity;
|
||||||
|
|
||||||
|
import me.polymarsdev.sokobot.event.CommandEvent;
|
||||||
|
|
||||||
|
public abstract class Command {
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
public Command(String name){
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract void execute(CommandEvent commandEvent);
|
||||||
|
}
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
package me.polymarsdev.sokobot.entity;
|
||||||
|
|
||||||
|
import me.polymarsdev.sokobot.objects.Grid;
|
||||||
|
|
||||||
|
public class Player {
|
||||||
|
int x = 0;
|
||||||
|
int y = 0;
|
||||||
|
Grid currentGrid;
|
||||||
|
|
||||||
|
public Player(int x, int y, Grid currentGrid) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
this.currentGrid = currentGrid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getX() {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getY() {
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void resetPosition() {
|
||||||
|
int setX = 2;
|
||||||
|
int setY = 2;
|
||||||
|
while (currentGrid.isBoxRaw(setX, setY)) {
|
||||||
|
if (setX >= currentGrid.getWidth() - 1) {
|
||||||
|
setY++;
|
||||||
|
setX = 1;
|
||||||
|
} else setX++;
|
||||||
|
}
|
||||||
|
this.x = setX;
|
||||||
|
this.y = setY;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean moveUp() {
|
||||||
|
if (!currentGrid.isWall(x, y - 1)) {
|
||||||
|
if (currentGrid.isBox(x, y - 1)) {
|
||||||
|
if (currentGrid.getBox(x, y - 1).moveUp()) {
|
||||||
|
y -= 1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
y -= 1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean moveDown() {
|
||||||
|
if (!currentGrid.isWall(x, y + 1)) {
|
||||||
|
if (currentGrid.isBox(x, y + 1)) {
|
||||||
|
if (currentGrid.getBox(x, y + 1).moveDown()) {
|
||||||
|
y += 1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
y += 1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean moveLeft() {
|
||||||
|
if (!currentGrid.isWall(x - 1, y)) {
|
||||||
|
if (currentGrid.isBox(x - 1, y)) {
|
||||||
|
if (currentGrid.getBox(x - 1, y).moveLeft()) {
|
||||||
|
x -= 1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
x -= 1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean moveRight() {
|
||||||
|
if (!currentGrid.isWall(x + 1, y)) {
|
||||||
|
if (currentGrid.isBox(x + 1, y)) {
|
||||||
|
if (currentGrid.getBox(x + 1, y).moveRight()) {
|
||||||
|
x += 1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
x += 1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,159 @@
|
|||||||
|
package me.polymarsdev.sokobot.event;
|
||||||
|
|
||||||
|
import net.dv8tion.jda.api.JDA;
|
||||||
|
import net.dv8tion.jda.api.entities.*;
|
||||||
|
import net.dv8tion.jda.api.events.message.guild.GuildMessageReceivedEvent;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
public class CommandEvent {
|
||||||
|
private static final int MAX_MESSAGES = 2;
|
||||||
|
|
||||||
|
private final GuildMessageReceivedEvent event;
|
||||||
|
private String[] args;
|
||||||
|
|
||||||
|
public CommandEvent(GuildMessageReceivedEvent event, String[] args) {
|
||||||
|
this.event = event;
|
||||||
|
this.args = args;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String[] getArgs() {
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GuildMessageReceivedEvent getEvent() {
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Member> getMentionedMembers() {
|
||||||
|
List<Member> mentionedMembers = new ArrayList<>(event.getMessage().getMentionedMembers());
|
||||||
|
if (event.getMessage().getContentRaw().startsWith("<@!" + event.getJDA().getSelfUser().getId() + ">"))
|
||||||
|
mentionedMembers.remove(0);
|
||||||
|
return mentionedMembers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reply(String message) {
|
||||||
|
sendMessage(event.getChannel(), message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reply(String message, Consumer<Message> success) {
|
||||||
|
sendMessage(event.getChannel(), message, success);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reply(String message, Consumer<Message> success, Consumer<Throwable> failure) {
|
||||||
|
sendMessage(event.getChannel(), message, success, failure);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reply(MessageEmbed embed) {
|
||||||
|
event.getChannel().sendMessage(embed).queue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reply(MessageEmbed embed, Consumer<Message> success) {
|
||||||
|
event.getChannel().sendMessage(embed).queue(success);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reply(MessageEmbed embed, Consumer<Message> success, Consumer<Throwable> failure) {
|
||||||
|
event.getChannel().sendMessage(embed).queue(success, failure);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reply(Message message) {
|
||||||
|
event.getChannel().sendMessage(message).queue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reply(Message message, Consumer<Message> success) {
|
||||||
|
event.getChannel().sendMessage(message).queue(success);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reply(Message message, Consumer<Message> success, Consumer<Throwable> failure) {
|
||||||
|
event.getChannel().sendMessage(message).queue(success, failure);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reply(File file, String filename) {
|
||||||
|
event.getChannel().sendFile(file, filename).queue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reply(String message, File file, String filename) {
|
||||||
|
String msg = message == null ? null : splitMessage(message).get(0);
|
||||||
|
if (msg == null) event.getChannel().sendFile(file, filename).queue();
|
||||||
|
else event.getChannel().sendMessage(msg).addFile(file, filename).queue();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendMessage(MessageChannel chan, String message) {
|
||||||
|
ArrayList<String> messages = splitMessage(message);
|
||||||
|
for (int i = 0; i < MAX_MESSAGES && i < messages.size(); i++) {
|
||||||
|
chan.sendMessage(messages.get(i)).queue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendMessage(MessageChannel chan, String message, Consumer<Message> success) {
|
||||||
|
ArrayList<String> messages = splitMessage(message);
|
||||||
|
for (int i = 0; i < MAX_MESSAGES && i < messages.size(); i++) {
|
||||||
|
if (i + 1 == MAX_MESSAGES || i + 1 == messages.size()) {
|
||||||
|
chan.sendMessage(messages.get(i)).queue(success);
|
||||||
|
} else {
|
||||||
|
chan.sendMessage(messages.get(i)).queue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendMessage(MessageChannel chan, String message, Consumer<Message> success,
|
||||||
|
Consumer<Throwable> failure) {
|
||||||
|
ArrayList<String> messages = splitMessage(message);
|
||||||
|
for (int i = 0; i < MAX_MESSAGES && i < messages.size(); i++) {
|
||||||
|
if (i + 1 == MAX_MESSAGES || i + 1 == messages.size()) {
|
||||||
|
chan.sendMessage(messages.get(i)).queue(success, failure);
|
||||||
|
} else {
|
||||||
|
chan.sendMessage(messages.get(i)).queue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ArrayList<String> splitMessage(String stringtoSend) {
|
||||||
|
ArrayList<String> msgs = new ArrayList<>();
|
||||||
|
if (stringtoSend != null) {
|
||||||
|
stringtoSend = stringtoSend.replace("@everyone", "@\u0435veryone").replace("@here", "@h\u0435re").trim();
|
||||||
|
while (stringtoSend.length() > 2000) {
|
||||||
|
int leeway = 2000 - (stringtoSend.length() % 2000);
|
||||||
|
int index = stringtoSend.lastIndexOf("\n", 2000);
|
||||||
|
if (index < leeway) index = stringtoSend.lastIndexOf(" ", 2000);
|
||||||
|
if (index < leeway) index = 2000;
|
||||||
|
String temp = stringtoSend.substring(0, index).trim();
|
||||||
|
if (!temp.equals("")) msgs.add(temp);
|
||||||
|
stringtoSend = stringtoSend.substring(index).trim();
|
||||||
|
}
|
||||||
|
if (!stringtoSend.equals("")) msgs.add(stringtoSend);
|
||||||
|
}
|
||||||
|
return msgs;
|
||||||
|
}
|
||||||
|
|
||||||
|
SelfUser getSelfUser() {
|
||||||
|
return event.getJDA().getSelfUser();
|
||||||
|
}
|
||||||
|
|
||||||
|
public User getAuthor() {
|
||||||
|
return event.getAuthor();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Guild getGuild() {
|
||||||
|
return event.getGuild();
|
||||||
|
}
|
||||||
|
|
||||||
|
public JDA getJDA() {
|
||||||
|
return event.getJDA();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Member getMember() {
|
||||||
|
return event.getMember();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Message getMessage() {
|
||||||
|
return event.getMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
public TextChannel getTextChannel() {
|
||||||
|
return event.getChannel();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
package me.polymarsdev.sokobot.listener;
|
||||||
|
|
||||||
|
import me.polymarsdev.sokobot.Bot;
|
||||||
|
import me.polymarsdev.sokobot.commands.GameInputCommand;
|
||||||
|
import me.polymarsdev.sokobot.commands.InfoCommand;
|
||||||
|
import me.polymarsdev.sokobot.commands.PrefixCommand;
|
||||||
|
import me.polymarsdev.sokobot.entity.Command;
|
||||||
|
import me.polymarsdev.sokobot.event.CommandEvent;
|
||||||
|
import net.dv8tion.jda.api.Permission;
|
||||||
|
import net.dv8tion.jda.api.entities.*;
|
||||||
|
import net.dv8tion.jda.api.events.message.guild.GuildMessageReceivedEvent;
|
||||||
|
import net.dv8tion.jda.api.hooks.ListenerAdapter;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public class CommandListener extends ListenerAdapter {
|
||||||
|
private static final ArrayList<String> commandsNoPrefix = new ArrayList<>(
|
||||||
|
Arrays.asList("w", "a", "s", "d", "up", "left", "down", "right", "r", "mr"));
|
||||||
|
private static final HashMap<String, Command> commands = new HashMap<>();
|
||||||
|
|
||||||
|
public CommandListener() {
|
||||||
|
List<Command> botCommands = new ArrayList<>(Arrays.asList(new InfoCommand(), new PrefixCommand()));
|
||||||
|
botCommands.addAll(Arrays.asList(new GameInputCommand("play"), new GameInputCommand("continue"),
|
||||||
|
new GameInputCommand("stop")));
|
||||||
|
for (String cnp : commandsNoPrefix) botCommands.add(new GameInputCommand(cnp));
|
||||||
|
for (Command command : botCommands) commands.put(command.getName().toLowerCase(), command);
|
||||||
|
System.out.println("[INFO] Loaded " + commands.size() + " commands");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onGuildMessageReceived(GuildMessageReceivedEvent event) {
|
||||||
|
User user = event.getAuthor();
|
||||||
|
Message message = event.getMessage();
|
||||||
|
TextChannel channel = event.getChannel();
|
||||||
|
Guild guild = event.getGuild();
|
||||||
|
String msgRaw = message.getContentRaw();
|
||||||
|
String[] args = msgRaw.split("\\s+");
|
||||||
|
if (args.length > 0) {
|
||||||
|
boolean isMention = msgRaw.equals("<@" + event.getJDA().getSelfUser().getId() + ">") || msgRaw
|
||||||
|
.equals("<@!" + event.getJDA().getSelfUser().getId() + ">");
|
||||||
|
String prefix = Bot.getPrefix(guild);
|
||||||
|
String arg = args[0].toLowerCase();
|
||||||
|
boolean isCommand;
|
||||||
|
if (isMention) isCommand = true;
|
||||||
|
else {
|
||||||
|
if (arg.startsWith(prefix)) {
|
||||||
|
if (commandsNoPrefix.contains(arg)) {
|
||||||
|
isCommand = true;
|
||||||
|
} else {
|
||||||
|
String commandName = arg.substring(prefix.length()).toLowerCase();
|
||||||
|
isCommand = commands.containsKey(commandName);
|
||||||
|
if (isCommand) arg = commandName;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
isCommand = commandsNoPrefix.contains(arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isCommand) {
|
||||||
|
Bot.debug("Command received: " + arg);
|
||||||
|
if (!hasPermissions(guild, channel)) {
|
||||||
|
Bot.debug("Not enough permissions to run command: " + arg);
|
||||||
|
sendInvalidPermissionsMessage(user, channel);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Command command = commands.get(arg);
|
||||||
|
if (isMention) command = commands.get("info");
|
||||||
|
if (command == null) {
|
||||||
|
Bot.debug("Received command does not exist: " + arg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Bot.debug("Executing command: " + arg);
|
||||||
|
command.execute(new CommandEvent(event, Arrays.copyOfRange(msgRaw.split("\\s+"), 1, args.length)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Collection<Permission> requiredPermissions = Arrays
|
||||||
|
.asList(Permission.MESSAGE_ADD_REACTION, Permission.MESSAGE_EMBED_LINKS, Permission.MESSAGE_MANAGE,
|
||||||
|
Permission.MESSAGE_WRITE);
|
||||||
|
|
||||||
|
private boolean hasPermissions(Guild guild, TextChannel channel) {
|
||||||
|
Member self = guild.getSelfMember();
|
||||||
|
if (self.hasPermission(Permission.ADMINISTRATOR)) return true;
|
||||||
|
return self.hasPermission(channel, requiredPermissions);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendInvalidPermissionsMessage(User user, TextChannel channel) {
|
||||||
|
if (channel.canTalk()) {
|
||||||
|
StringBuilder requiredPermissionsDisplay = new StringBuilder();
|
||||||
|
for (Permission requiredPermission : requiredPermissions) {
|
||||||
|
requiredPermissionsDisplay.append("`").append(requiredPermission.getName()).append("`, ");
|
||||||
|
}
|
||||||
|
if (requiredPermissionsDisplay.toString().endsWith(", ")) requiredPermissionsDisplay = new StringBuilder(
|
||||||
|
requiredPermissionsDisplay.substring(0, requiredPermissionsDisplay.length() - 2));
|
||||||
|
channel.sendMessage(user.getAsMention() + ", I don't have enough permissions to work properly.\nMake "
|
||||||
|
+ "sure I have the following permissions: " + requiredPermissionsDisplay
|
||||||
|
+ "\nIf you think this is "
|
||||||
|
+ "an error, please contact a server administrator.").queue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
package me.polymarsdev.sokobot.listener;
|
||||||
|
|
||||||
|
import me.polymarsdev.sokobot.Bot;
|
||||||
|
import me.polymarsdev.sokobot.Game;
|
||||||
|
import me.polymarsdev.sokobot.util.GameUtil;
|
||||||
|
import net.dv8tion.jda.api.Permission;
|
||||||
|
import net.dv8tion.jda.api.entities.Guild;
|
||||||
|
import net.dv8tion.jda.api.entities.MessageReaction;
|
||||||
|
import net.dv8tion.jda.api.entities.TextChannel;
|
||||||
|
import net.dv8tion.jda.api.entities.User;
|
||||||
|
import net.dv8tion.jda.api.events.guild.GuildLeaveEvent;
|
||||||
|
import net.dv8tion.jda.api.events.message.guild.react.GuildMessageReactionAddEvent;
|
||||||
|
import net.dv8tion.jda.api.hooks.ListenerAdapter;
|
||||||
|
|
||||||
|
public class GameListener extends ListenerAdapter {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onGuildLeave(GuildLeaveEvent event) {
|
||||||
|
Guild guild = event.getGuild();
|
||||||
|
Bot.removePrefix(guild.getIdLong());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onGuildMessageReactionAdd(GuildMessageReactionAddEvent event) {
|
||||||
|
User user = event.getUser();
|
||||||
|
if (user.isBot()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Guild guild = event.getGuild();
|
||||||
|
MessageReaction reaction = event.getReaction();
|
||||||
|
TextChannel channel = event.getChannel();
|
||||||
|
channel.retrieveMessageById(event.getMessageId()).queue(message -> {
|
||||||
|
if (message.getAuthor().getId().equals(event.getJDA().getSelfUser().getId())) {
|
||||||
|
Game game;
|
||||||
|
if (!GameUtil.hasGame(user.getIdLong())) {
|
||||||
|
game = new Game(user);
|
||||||
|
GameUtil.setGame(user.getIdLong(), game);
|
||||||
|
} else game = GameUtil.getGame(user.getIdLong());
|
||||||
|
boolean reactionCommand = true;
|
||||||
|
String userInput = "";
|
||||||
|
switch (event.getReactionEmote().toString()) {
|
||||||
|
case "RE:U+2b05":
|
||||||
|
userInput = "left";
|
||||||
|
break;
|
||||||
|
case "RE:U+27a1":
|
||||||
|
userInput = "right";
|
||||||
|
break;
|
||||||
|
case "RE:U+2b06":
|
||||||
|
userInput = "up";
|
||||||
|
break;
|
||||||
|
case "RE:U+2b07":
|
||||||
|
userInput = "down";
|
||||||
|
break;
|
||||||
|
case "RE:U+1f504":
|
||||||
|
userInput = "r";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
reactionCommand = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Bot.debug("Executing reaction input: " + userInput);
|
||||||
|
if (reactionCommand) {
|
||||||
|
game.run(guild, channel, userInput);
|
||||||
|
} else Bot.debug("Received invalid reaction command: " + event.getReactionEmote().getName());
|
||||||
|
if (guild.getSelfMember().hasPermission(channel, Permission.MESSAGE_MANAGE))
|
||||||
|
reaction.removeReaction(user).queue();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
package me.polymarsdev.sokobot.objects;
|
||||||
|
|
||||||
public class Box
|
public class Box
|
||||||
{
|
{
|
||||||
int x = 0;
|
int x = 0;
|
||||||
+11
-10
@@ -1,24 +1,25 @@
|
|||||||
public class Destination
|
package me.polymarsdev.sokobot.objects;
|
||||||
{
|
|
||||||
|
public class Destination {
|
||||||
int x = 0;
|
int x = 0;
|
||||||
int y = 0;
|
int y = 0;
|
||||||
Grid currentGrid;
|
Grid currentGrid;
|
||||||
public Destination(int x, int y, Grid currentGrid)
|
|
||||||
{
|
public Destination(int x, int y, Grid currentGrid) {
|
||||||
this.x = x;
|
this.x = x;
|
||||||
this.y = y;
|
this.y = y;
|
||||||
this.currentGrid = currentGrid;
|
this.currentGrid = currentGrid;
|
||||||
}
|
}
|
||||||
public boolean hasBox(Grid currentGrid)
|
|
||||||
{
|
public boolean hasBox(Grid currentGrid) {
|
||||||
return currentGrid.isWall(x, y);
|
return currentGrid.isWall(x, y);
|
||||||
}
|
}
|
||||||
public int getX()
|
|
||||||
{
|
public int getX() {
|
||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
public int getY()
|
|
||||||
{
|
public int getY() {
|
||||||
return y;
|
return y;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,206 @@
|
|||||||
|
package me.polymarsdev.sokobot.objects;
|
||||||
|
|
||||||
|
import me.polymarsdev.sokobot.entity.Player;
|
||||||
|
import me.polymarsdev.sokobot.util.Randomizer;
|
||||||
|
|
||||||
|
public class Grid {
|
||||||
|
final int GROUND = 0;
|
||||||
|
final int WALL = 1;
|
||||||
|
final int BOX = 2;
|
||||||
|
final int DESTINATION = 3;
|
||||||
|
final int PLAYER = 4;
|
||||||
|
final int MAX_BOXES = 8;
|
||||||
|
Tile[][] grid;
|
||||||
|
Box[] boxes;
|
||||||
|
Destination[] destinations;
|
||||||
|
int boxCount;
|
||||||
|
int height = 0;
|
||||||
|
int width = 0;
|
||||||
|
int color = 0;
|
||||||
|
Player player;
|
||||||
|
String playerEmote;
|
||||||
|
|
||||||
|
public Grid(int width, int height, int boxCount, String playerEmote) //create a random grid with specific width,
|
||||||
|
// height, and number of boxes
|
||||||
|
{
|
||||||
|
this.playerEmote = playerEmote;
|
||||||
|
player = new Player(2, 2, this);
|
||||||
|
if (boxCount > MAX_BOXES) boxCount = MAX_BOXES;
|
||||||
|
this.boxCount = boxCount;
|
||||||
|
boxes = new Box[boxCount];
|
||||||
|
destinations = new Destination[boxCount];
|
||||||
|
this.height = height;
|
||||||
|
this.width = width;
|
||||||
|
grid = new Tile[width][height];
|
||||||
|
createBoxes();
|
||||||
|
createDestinations();
|
||||||
|
player.resetPosition();
|
||||||
|
updateGrid();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Player getPlayer() {
|
||||||
|
return player;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reset() {
|
||||||
|
for (int i = 0; i < boxCount; i++) {
|
||||||
|
boxes[i].reset();
|
||||||
|
}
|
||||||
|
player.resetPosition();
|
||||||
|
updateGrid();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void resetMap() {
|
||||||
|
boxes = new Box[boxCount];
|
||||||
|
destinations = new Destination[boxCount];
|
||||||
|
createBoxes();
|
||||||
|
createDestinations();
|
||||||
|
player.resetPosition();
|
||||||
|
updateGrid();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStatus(int x, int y, int status) {
|
||||||
|
grid[x][y].setStatus(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getStatus(int x, int y) {
|
||||||
|
return grid[x][y].getStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isWall(int x, int y) {
|
||||||
|
return grid[x][y].getStatus() == WALL;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isBox(int x, int y) {
|
||||||
|
return grid[x][y].getStatus() == BOX;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isBoxRaw(int x, int y) //allows you to check if a box is at a position before grid is set up
|
||||||
|
{
|
||||||
|
for (int i = 0; i < boxCount; i++) {
|
||||||
|
if (x == boxes[i].getX() && y == boxes[i].getY()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isDestination(int x, int y) {
|
||||||
|
return grid[x][y].getStatus() == DESTINATION;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public Box getBox(int x, int y) {
|
||||||
|
for (int i = 0; i < boxCount; i++) {
|
||||||
|
if (x == boxes[i].getX() && y == boxes[i].getY()) {
|
||||||
|
return boxes[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void createBoxes() {
|
||||||
|
color = Randomizer.nextInt(6); //runs after each level
|
||||||
|
for (int i = 0; i < boxCount; i++) {
|
||||||
|
int x = Randomizer.nextInt(width - 4) + 2;
|
||||||
|
int y = Randomizer.nextInt(height - 4) + 2;
|
||||||
|
for (int j = 0; j < i; j++) {
|
||||||
|
while ((x == boxes[j].getX() && y == boxes[j].getY()) || (x == 2 && y == 2) || (x - 1 == boxes[j].getX()
|
||||||
|
&& y == boxes[j].getY()) || (x + 1 == boxes[j].getX() && y == boxes[j].getY())) {
|
||||||
|
x = Randomizer.nextInt(width - 4) + 2;
|
||||||
|
y = Randomizer.nextInt(height - 4) + 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
boxes[i] = new Box(x, y, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void createDestinations() {
|
||||||
|
for (int i = 0; i < boxCount; i++) {
|
||||||
|
int x = Randomizer.nextInt(width - 2) + 1;
|
||||||
|
int y = Randomizer.nextInt(height - 2) + 1;
|
||||||
|
for (int j = 0; j < i; j++) {
|
||||||
|
while (((x == destinations[j].getX() && y == destinations[j].getY())) || isBoxRaw(x, y)) {
|
||||||
|
x = Randomizer.nextInt(width - 2) + 1;
|
||||||
|
y = Randomizer.nextInt(height - 2) + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
destinations[i] = new Destination(x, y, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateGrid() {
|
||||||
|
for (int i = 0; i < height; i++) {
|
||||||
|
for (int j = 0; j < width; j++) {
|
||||||
|
grid[j][i] = new Tile(GROUND, playerEmote);
|
||||||
|
if (j == 0 || j == width - 1 || i == 0 || i == height - 1) {
|
||||||
|
grid[j][i] = new Tile(WALL, color, playerEmote);
|
||||||
|
}
|
||||||
|
for (int k = 0; k < boxCount; k++) {
|
||||||
|
if (destinations[k].getX() == j && destinations[k].getY() == i) {
|
||||||
|
grid[j][i] = new Tile(DESTINATION, playerEmote);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (player.getX() == j && player.getY() == i) {
|
||||||
|
grid[j][i] = new Tile(PLAYER, playerEmote);
|
||||||
|
}
|
||||||
|
for (int k = 0; k < boxCount; k++) {
|
||||||
|
if (boxes[k].getX() == j && boxes[k].getY() == i) {
|
||||||
|
if (boxes[k].onDestination()) {
|
||||||
|
grid[j][i] = new Tile(WALL, color, playerEmote);
|
||||||
|
} else {
|
||||||
|
grid[j][i] = new Tile(BOX, playerEmote);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasWon() {
|
||||||
|
for (int i = 0; i < boxCount; i++) {
|
||||||
|
if (!destinations[i].hasBox(this)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Tile[][] getGrid() {
|
||||||
|
return grid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Box[] getBoxes() {
|
||||||
|
return boxes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Destination[] getDestinations() {
|
||||||
|
return destinations;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getBoxCount() {
|
||||||
|
return boxCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getHeight() {
|
||||||
|
return height;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getWidth() {
|
||||||
|
return width;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
updateGrid();
|
||||||
|
String result = "";
|
||||||
|
for (int i = 0; i < height; i++) {
|
||||||
|
for (int j = 0; j < width; j++) {
|
||||||
|
result += grid[j][i];
|
||||||
|
}
|
||||||
|
result += "\n";
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
package me.polymarsdev.sokobot.objects;
|
||||||
|
|
||||||
public class Tile
|
public class Tile
|
||||||
{
|
{
|
||||||
final int GROUND = 0;
|
final int GROUND = 0;
|
||||||
@@ -7,14 +9,17 @@ public class Tile
|
|||||||
final int PLAYER = 4;
|
final int PLAYER = 4;
|
||||||
int color = 0;
|
int color = 0;
|
||||||
int status = 0;
|
int status = 0;
|
||||||
public Tile(int status)
|
String playerEmote;
|
||||||
|
public Tile(int status, String playerEmote)
|
||||||
{
|
{
|
||||||
this.status = status;
|
this.status = status;
|
||||||
|
this.playerEmote = playerEmote;
|
||||||
}
|
}
|
||||||
public Tile(int status, int color)
|
public Tile(int status, int color, String playerEmote)
|
||||||
{
|
{
|
||||||
this.status = status;
|
this.status = status;
|
||||||
this.color = color;
|
this.color = color;
|
||||||
|
this.playerEmote = playerEmote;
|
||||||
}
|
}
|
||||||
public void setStatus(int status)
|
public void setStatus(int status)
|
||||||
{
|
{
|
||||||
@@ -60,6 +65,6 @@ public class Tile
|
|||||||
{
|
{
|
||||||
return ":negative_squared_cross_mark:";
|
return ":negative_squared_cross_mark:";
|
||||||
}
|
}
|
||||||
return ":flushed:";
|
return playerEmote;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
package me.polymarsdev.sokobot.util;
|
||||||
|
|
||||||
|
import me.polymarsdev.sokobot.Bot;
|
||||||
|
import me.polymarsdev.sokobot.Game;
|
||||||
|
import net.dv8tion.jda.api.EmbedBuilder;
|
||||||
|
import net.dv8tion.jda.api.entities.Guild;
|
||||||
|
import net.dv8tion.jda.api.entities.Message;
|
||||||
|
import net.dv8tion.jda.api.entities.MessageChannel;
|
||||||
|
import net.dv8tion.jda.api.entities.User;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Timer;
|
||||||
|
import java.util.TimerTask;
|
||||||
|
|
||||||
|
public class GameUtil {
|
||||||
|
|
||||||
|
private static final HashMap<Long, Game> games = new HashMap<>();
|
||||||
|
|
||||||
|
public static void setGame(long userId, Game game) {
|
||||||
|
games.put(userId, game);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean hasGame(long userId) {
|
||||||
|
return games.containsKey(userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Game getGame(long userId) {
|
||||||
|
return games.get(userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void removeGame(long userId) {
|
||||||
|
games.remove(userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void sendGameEmbed(MessageChannel channel, String level, String game, User user) {
|
||||||
|
EmbedBuilder embed = new EmbedBuilder();
|
||||||
|
embed.setTitle("Sokobot | Level " + level);
|
||||||
|
embed.setDescription(game);
|
||||||
|
embed.addField("Enter direction (``up``, ``down``, ``left``, ``right``/``wasd``), ``r`` to reset or ``mr`` to "
|
||||||
|
+ "recreate the map", "", false);
|
||||||
|
embed.addField("Player", user.getAsMention(), false);
|
||||||
|
channel.sendMessage(embed.build()).queue(message -> {
|
||||||
|
message.addReaction("U+2B05").queue();
|
||||||
|
message.addReaction("U+27A1").queue();
|
||||||
|
message.addReaction("U+2B06").queue();
|
||||||
|
message.addReaction("U+2B07").queue();
|
||||||
|
message.addReaction("U+1F504").queue();
|
||||||
|
Game theGame = GameUtil.getGame(user.getIdLong());
|
||||||
|
theGame.setGameMessage(message);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void updateGameEmbed(Message message, String level, String game, User user) {
|
||||||
|
EmbedBuilder embed = new EmbedBuilder();
|
||||||
|
embed.setTitle("Sokobot | Level " + level);
|
||||||
|
embed.setDescription(game);
|
||||||
|
embed.addField("Enter direction (``up``, ``down``, ``left``, ``right``/``wasd``), ``r`` to reset or ``mr`` to "
|
||||||
|
+ "recreate the map", "", false);
|
||||||
|
embed.addField("Player", user.getAsMention(), false);
|
||||||
|
message.editMessage(embed.build()).queue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void sendWinEmbed(Guild guild, Message message, String level) {
|
||||||
|
EmbedBuilder embed = new EmbedBuilder();
|
||||||
|
embed.setTitle("Sokobot | You win!");
|
||||||
|
embed.setDescription(
|
||||||
|
"Type ``" + Bot.getPrefix(guild) + "continue`` to continue to Level " + level + " or ``" + Bot
|
||||||
|
.getPrefix(guild) + "stop`` to quit ");
|
||||||
|
embed.setFooter("You can also press any reaction to continue.");
|
||||||
|
message.editMessage(embed.build()).queue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void runGameTimer() {
|
||||||
|
new Timer().scheduleAtFixedRate(new TimerTask() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
for (long playerId : games.keySet()) {
|
||||||
|
Game game = games.get(playerId);
|
||||||
|
long timeDifference = now - game.lastAction;
|
||||||
|
if (timeDifference > 10 * 60 * 1000) {
|
||||||
|
System.out.println("[INFO] Stopped inactive game of " + playerId);
|
||||||
|
game.stop();
|
||||||
|
GameUtil.removeGame(playerId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 10 * 60 * 1000, 60 * 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
+2
-4
@@ -1,13 +1,11 @@
|
|||||||
|
package me.polymarsdev.sokobot.util;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
public class Randomizer{
|
public class Randomizer{
|
||||||
|
|
||||||
public static Random theInstance = null;
|
public static Random theInstance = null;
|
||||||
|
|
||||||
public Randomizer(){
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Random getInstance(){
|
public static Random getInstance(){
|
||||||
if(theInstance == null){
|
if(theInstance == null){
|
||||||
theInstance = new Random();
|
theInstance = new Random();
|
||||||
Reference in New Issue
Block a user