discord-battleship
Version:
Create and handle advanced versions of the old classic game, BatlleShip!
328 lines • 24.3 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.DiscordBattleShip = void 0;
const discord_js_1 = require("discord.js");
class DiscordBattleShip {
constructor(settings) {
this.settings = settings;
if (!this.settings.embedColor)
this.settings.embedColor = "#6b8ba4";
}
;
async createGame(message) {
const challenger = message.member; // Define the challenger
const opponent = message.mentions.members.first(); // Get and define the opponent
if (!opponent)
return message.channel.send("Please mention a member to battle!"); // If there is no opponent, require them to define one
if (challenger.id === opponent.id)
return message.channel.send("Please challenge someone other than yourself!"); // Check for prevention against challenging yourself
const accept = await message.channel.send(`Hey ${opponent}, ${challenger.user.tag} just challenged you to a game of Battle Ship. Do you accept?`); // Ask if the user would like to play
await Promise.all([accept.react("✅"), accept.react("❌")]);
const acceptFilter = (reaction, user) => user.id === opponent.id && ["✅", "❌"].includes(reaction.emoji.name);
const acceptedData = await accept.awaitReactions(acceptFilter, { max: 1, time: 30000 });
if (acceptedData.size < 1)
return accept.edit("They didn't react in time, looks like they declined.");
if (acceptedData.first().emoji.name === "❌")
return accept.edit("Looks like they declined. \:(");
await accept.delete();
const trackingEmbed = new discord_js_1.MessageEmbed()
.setTitle("Battle Ship Game")
.setFooter(`${challenger.user.tag} vs ${opponent.user.tag}`)
.setColor(this.settings.embedColor)
.setDescription("The game has begun! Check your DM's for instructions on how to proceed. This embed will update as the game continues.");
const trackMsg = await message.channel.send("", { embed: trackingEmbed });
const players = [
{ collector: null, member: challenger, playerHitBoard: this.genBoard(10, 10), playerShipBoard: this.genBoard(10, 10), gameChannel: "", placedBoats: [], gameMessages: { start: "", hits: "", boats: "" }, ready: false },
{ collector: null, member: opponent, playerHitBoard: this.genBoard(10, 10), playerShipBoard: this.genBoard(10, 10), gameChannel: "", placedBoats: [], gameMessages: { start: "", hits: "", boats: "" }, ready: false },
];
let player = 0;
for (const play of players) {
const startMsg = await play.member.send(`Here is your starting Attack and Ship board! To add your boat pieces to the Ship board, please use the following command format. \`${this.settings.prefix}add <ship> <Board Cords> <dirrection>\`. An example of this would be, \`${this.settings.prefix}add destroyer D5 down\`\n\nAvailable Ships:\ncarrier (5)\nbattleship (4)\ndestroyer (3)\nsubmarine (3)\npatrolboat (2)`);
const hitBoard = await play.member.send(`Attack Board:\n${this.displayBoard(play.playerHitBoard, "hit")}`);
const dmBoard = await play.member.send(`Ship Board:\n${this.displayBoard(play.playerShipBoard, "ship")}`);
play.gameMessages.start = startMsg.id;
play.gameMessages.hits = hitBoard.id;
play.gameMessages.boats = dmBoard.id;
const filter = (msg) => msg.author.id === play.member.id && [`${this.settings.prefix}add`, `${this.settings.prefix}attack`].includes(msg.content.split(" ")[0]);
const dmCollector = dmBoard.channel.createMessageCollector(filter);
play.collector = dmCollector;
play.gameChannel = dmBoard.channel.id;
const validBoats = [{ name: "carrier", length: 5, hits: 0, sunk: false }, { name: "battleship", length: 4, hits: 0, sunk: false }, { name: "destroyer", length: 3, hits: 0, sunk: false }, { name: "submarine", length: 3, hits: 0, sunk: false }, { name: "patrolboat", length: 2, hits: 0, sunk: false }];
const validDirrections = ["up", "down", "right", "left"];
dmCollector.on("collect", async (msg) => {
const argument = msg.content.slice(this.settings.prefix.length).trim().split(/ +/g);
const cmd = argument.shift();
if (!players.find(plr => plr.member.id === msg.author.id).ready) {
if (cmd === "add") {
const currPlayer = players.find(plr => plr.member.id === msg.author.id);
const gameChannelObject = message.client.channels.cache.get(currPlayer.gameChannel);
const boatType = argument[0];
if (!boatType)
return msg.channel.send("Please provide a boat to place.").then(m => m.delete({ timeout: 15000 }));
if (!validBoats.some(value => value.name === boatType.toLowerCase()))
return msg.channel.send("Please provide a valid boat type to place.").then(m => m.delete({ timeout: 15000 }));
if (players.find(plyr => plyr.member.id === msg.author.id).placedBoats.some(data => data.name === boatType.toLowerCase()))
return msg.channel.send("You already placed that boat. Please try a different one.").then(m => m.delete({ timeout: 15000 }));
const cords = argument[1];
if (!cords)
return msg.channel.send("Please enter cords for your ship. Ex: `D5`").then(m => m.delete({ timeout: 15000 }));
const directionRegex = /[a-z]([1-9]|10)/i;
if (!cords.match(directionRegex))
return msg.channel.send("Please enter valid cords for your ship. Ex: `D5`").then(m => m.delete({ timeout: 15000 }));
const dirrection = argument[2];
if (!dirrection)
return msg.channel.send("Please provide a direction to position your boat!").then(m => m.delete({ timeout: 15000 }));
if (!validDirrections.some(value => value === dirrection.toLowerCase()))
return msg.channel.send(`Please provide a valid dirrection. Valid Choices: ${validDirrections.join(", ")}`).then(m => m.delete({ timeout: 15000 }));
const checked = this.checkBoatPos(play.playerShipBoard, validBoats.find(data => data.name === boatType.toLowerCase()), { letter: cords[0], number: parseInt(cords.slice(1)), cord: cords }, dirrection, "check");
if (!checked)
return msg.channel.send(`You can't put the ${boatType} at ${cords} facing ${dirrection}`).then(m => m.delete({ timeout: 15000 }));
currPlayer.placedBoats.push(validBoats.find(data => data.name === boatType.toLowerCase()));
const reRender = this.checkBoatPos(players.find(plyr => plyr.member.id === msg.author.id).playerShipBoard, validBoats.find(boat => boat.name === boatType.toLowerCase()), { letter: cords[0], number: parseInt(cords.slice(1)), cord: cords }, dirrection, "render");
currPlayer.playerShipBoard = reRender.board;
gameChannelObject.messages.cache.get(currPlayer.gameMessages.boats).edit(`Ship Board:\n${this.displayBoard(reRender.board, "ship")}`);
let oldBoat = players.find(p => p.member.id === msg.author.id).placedBoats.find(b => b.name.toLowerCase() === reRender.boat.name.toLowerCase());
oldBoat = reRender.boat;
const editMe = gameChannelObject.messages.cache.get(currPlayer.gameMessages.start);
const regex = new RegExp(boatType.toLowerCase(), "ig");
editMe.edit(editMe.content.replace(regex, `~~${boatType.toLowerCase()}~~`));
if (currPlayer.placedBoats.length === 5) {
currPlayer.ready = true;
if (players[0].ready && players[1].ready) {
for (const playr of players) {
const perGame = message.client.channels.cache.get(playr.gameChannel);
perGame.messages.cache.get(playr.gameMessages.start).edit(`You have both now finished the setup phase of the game! It is ${players[player].member.user.tag}'s turn to attack! Use \`${this.settings.prefix}attack <cords>\` to call an attack on that spot!\n\nLegend:\n- Attack Board:\n--- ◻️ = Empty Spot\n--- ⚪ = Missed Attack\n--- 🔴 = Hit Attack\n- Ship Board:\n--- 🔲 = Empty Spot\n--- 🟩 = Unhit Ship\n--- 🟥 = Hit Ship\n--- ⚪ = Missed Opponent Shot`);
playr.member.send(`${playr.member.user}`).then(m => m.delete());
}
trackingEmbed.setDescription("");
for (const p of players) {
trackingEmbed.addField(p.member.user.tag, `Has ${p.placedBoats.filter(b => !b.sunk).length} ships left!\n\n${p.placedBoats.map(b => b.sunk ? `❌ ${b.name}` : `✅ ${b.name}`).join("\n")}`);
}
trackMsg.edit(trackingEmbed);
}
else
return msg.channel.send("It looks like your opponent hasn't placed all of their ships yet! Please wait for them to finish. Once they finish you will get a DM.").then(m => m.delete({ timeout: 15000 }));
}
}
}
else if (players[0].ready && players[1].ready) {
if (players[player].member.id === msg.author.id) {
if (cmd === "attack") {
const playerChannel = message.client.channels.cache.get(players[player].gameChannel);
const opponentChannel = message.client.channels.cache.get(players[(player + 1) % players.length].gameChannel);
const cords = argument[0];
if (!cords)
return msg.channel.send("Please enter cords for your attack. Ex: `D5`").then(m => m.delete({ timeout: 15000 }));
const directionRegex = /[a-z]([1-9]|10)/i;
if (!cords.match(directionRegex))
return msg.channel.send("Please enter valid cords for your attack. Ex: `D5`").then(m => m.delete({ timeout: 15000 }));
const returnData = this.attack(players[player].playerHitBoard, players[(player + 1) % players.length].playerShipBoard, { letter: cords[0], number: parseInt(cords.slice(1)), cord: cords });
if (!returnData)
return msg.channel.send("You can't attack there, please try somewhere else!").then(m => m.delete({ timeout: 15000 }));
playerChannel.messages.cache.get(players[player].gameMessages.hits).edit(`Attack Board:\n${this.displayBoard(returnData.attackBoard, "hit")}`);
players[player].playerHitBoard = returnData.attackBoard;
opponentChannel.messages.cache.get(players[(player + 1) % players.length].gameMessages.boats).edit(`Ship Board:\n${this.displayBoard(returnData.shipBoard, "ship")}`);
players[(player + 1) % 2].playerShipBoard = returnData.shipBoard;
const shipToHit = players[(player + 1) % players.length].placedBoats.find(s => s.name.toLowerCase() === returnData.shipName.toLowerCase());
if (shipToHit) {
shipToHit.hits++;
if (shipToHit.hits === shipToHit.length) {
shipToHit.sunk = true;
players[player].member.send(`You sunk ${players[(player + 1) % players.length].member.user.tag}'s ${shipToHit.name}!`);
players[(player + 1) % players.length].member.send(`Your ${returnData.shipName} was sunk!`);
const embed = new discord_js_1.MessageEmbed()
.setTitle("Battle Ship Game <:submarine:753289857907818561>")
.setFooter(`${challenger.user.tag} vs ${opponent.user.tag}`)
.setColor(this.settings.embedColor);
for (const p of players) {
embed.addField(p.member.user.tag, `Has ${p.placedBoats.filter(b => !b.sunk).length} ships left!\n\n${p.placedBoats.map(b => b.sunk ? `❌ ${b.name}` : `✅ ${b.name}`).join("\n")}`);
}
trackMsg.edit("", { embed });
}
}
if (this.winCondition(players[(player + 1) % players.length].placedBoats)) {
for (const p of players) {
p.collector.stop();
p.member.send(`${players[player].member.user} won the game!`);
}
const embed = new discord_js_1.MessageEmbed()
.setTitle("Battle Ship Game <:submarine:753289857907818561>")
.setFooter(`${challenger.user.tag} vs ${opponent.user.tag}`)
.setColor(this.settings.embedColor)
.setDescription(`${players[player].member.user} has won the game!`);
trackMsg.edit(`${players[0].member}, ${players[1].member}`, { embed });
}
playerChannel.messages.cache.get(players[player].gameMessages.start).edit(`It is now ${players[(player + 1) % players.length].member.user.tag}'s turn! Use \`${this.settings.prefix}attack <cords>\` to call an attack on that spot!\n\nLegend:\n- Attack Board:\n--- ◻️ = Empty Spot\n--- ⚪ = Missed Attack\n--- 🔴 = Hit Attack\n- Ship Board:\n--- 🔲 = Empty Spot\n--- 🟩 = Unhit Ship\n--- 🟥 = Hit Ship\n--- ⚪ = Missed Opponent Shot`);
opponentChannel.messages.cache.get(players[(player + 1) % players.length].gameMessages.start).edit(`It is now ${players[(player + 1) % players.length].member.user.tag}'s turn! Use \`${this.settings.prefix}attack <cords>\` to call an attack on that spot!\n\nLegend:\n- Attack Board:\n--- ◻️ = Empty Spot\n--- ⚪ = Missed Attack\n--- 🔴 = Hit Attack\n- Ship Board:\n--- 🔲 = Empty Spot\n--- 🟩 = Unhit Ship\n--- 🟥 = Hit Ship\n--- ⚪ = Missed Opponent Shot`);
player = (player + 1) % players.length;
players[player].member.send(`${players[player].member.user}`).then(m => m.delete());
}
}
else
return msg.channel.send("It isn't your turn yet. Please wait for the opponent to attack.").then(m => m.delete({ timeout: 10000 }));
}
else
return msg.channel.send("It looks like the opponent/you hasn't/haven't placed all their/your ships. Please either finish placing your ships or wait for your opponent to finish!").then(m => m.delete({ timeout: 10000 }));
});
}
return message.channel.send("In progress.");
}
winCondition(boats) {
for (const playerBoat of boats) {
if (!playerBoat.sunk)
return false;
}
return true;
}
attack(attackBoard, shipBoard, spot) {
let shipName = "";
for (let i = 0; i < shipBoard.length; i++) {
const index = shipBoard[i].findIndex(data => data.cords.cord.toLowerCase() === spot.cord.toLowerCase());
if (shipBoard[i].find(data => data.cords.cord.toLowerCase() === spot.cord.toLowerCase())) {
// Missed attack
if (shipBoard[i][index].data === "0") {
shipBoard[i][index].data = "3";
attackBoard[i][index].data = "1";
// Successful attack
}
else if (shipBoard[i][index].data === "1") {
shipBoard[i][index].data = "2";
attackBoard[i][index].data = "2";
shipName = shipBoard[i][index].ship;
}
else
return false;
}
}
return { shipBoard, attackBoard, shipName };
}
checkBoatPos(board, boat, cords, dirrection, type) {
for (let i = 0; i < board.length; i++) {
if (board[i].find(data => data.cords.cord.toLowerCase() === cords.cord.toLowerCase())) {
switch (dirrection) {
case "up":
let countUp = 0;
let startPosUp = i;
do {
if (type === "check") {
if (board[startPosUp] === undefined)
return;
if (board[startPosUp][cords.number - 1].data === "1")
return;
countUp++;
startPosUp--;
}
else {
board[startPosUp][cords.number - 1].data = "1";
board[startPosUp][cords.number - 1].ship = boat.name;
countUp++;
startPosUp--;
}
} while (countUp < boat.length);
break;
case "down":
let countDown = 0;
let startPosDown = i;
do {
if (type === "check") {
if (board[startPosDown] === undefined)
return;
if (board[startPosDown][cords.number - 1].data === "1")
return;
countDown++;
startPosDown++;
}
else {
board[startPosDown][cords.number - 1].data = "1";
board[startPosDown][cords.number - 1].ship = boat.name;
countDown++;
startPosDown++;
}
} while (countDown < boat.length);
break;
case "left":
let countLeft = 0;
let currIndexLeft = board[i].findIndex(data => data.cords.cord.toLowerCase() === cords.cord.toLowerCase());
do {
if (type === "check") {
currIndexLeft--;
if (board[i][currIndexLeft] === undefined)
return;
if (board[i][currIndexLeft].data === "1")
return;
countLeft++;
}
else {
board[i][currIndexLeft].data = "1";
board[i][currIndexLeft].ship = boat.name;
currIndexLeft--;
countLeft++;
}
} while (countLeft < boat.length);
break;
case "right":
let countRight = 0;
let currIndexRight = board[i].findIndex(data => data.cords.cord.toLowerCase() === cords.cord.toLowerCase());
do {
if (type === "check") {
currIndexRight++;
if (board[i][currIndexRight] === undefined)
return;
if (board[i][currIndexRight].data === "1")
return;
countRight++;
}
else {
board[i][currIndexRight].data = "1";
board[i][currIndexRight].ship = boat.name;
currIndexRight++;
countRight++;
}
} while (countRight < boat.length);
break;
}
}
}
return { boat, board };
}
genBoard(hor, ver) {
let whileCounter = 0;
const boardLetter = [{ i: 0, letter: "A" }, { i: 1, letter: "B" }, { i: 2, letter: "C" }, { i: 3, letter: "D" }, { i: 4, letter: "E" }, { i: 5, letter: "F" }, { i: 6, letter: "G" }, { i: 7, letter: "H" }, { i: 8, letter: "I" }, { i: 9, letter: "J" }];
const doneData = [];
do {
const temp = [];
for (let i = 0; i < ver; i++) {
const boardLttr = boardLetter.find(data => data.i === whileCounter).letter;
temp.push({ data: "0", ship: "", cords: { letter: boardLttr, number: i + 1, cord: boardLttr + (i + 1) } });
}
doneData.push(temp);
whileCounter++;
} while (whileCounter < hor);
return doneData;
}
displayBoard(board, type) {
let returnData = "";
returnData = returnData.concat("⬛1️⃣2️⃣3️⃣4️⃣5️⃣6️⃣7️⃣8️⃣9️⃣🔟\n");
for (let i = 0; i < board.length; i++) {
let temp = "";
const leftEmoji = [{ i: 0, emoji: ":regional_indicator_a:" }, { i: 1, emoji: ":regional_indicator_b:" }, { i: 2, emoji: ":regional_indicator_c:" }, { i: 3, emoji: ":regional_indicator_d:" }, { i: 4, emoji: ":regional_indicator_e:" }, { i: 5, emoji: ":regional_indicator_f:" }, { i: 6, emoji: ":regional_indicator_g:" }, { i: 7, emoji: ":regional_indicator_h:" }, { i: 8, emoji: ":regional_indicator_i:" }, { i: 9, emoji: ":regional_indicator_j:" }];
if (type === "hit") {
for (let j = 0; j < board[i].length; j++) {
// "0" is an empty space, "1" is a missed shot, "2" is a hit shot
temp += `${board[i][j].data === "0" ? "◻️" : board[i][j].data === "1" ? "⚪" : "🔴"}`;
}
}
else {
for (let j = 0; j < board[i].length; j++) {
// "0" is an empty space, "1" is a unhit ship piece, "2" is a hit ship piece, "3" is a missed shot from opponent
temp += `${board[i][j].data === "0" ? "◻️" : board[i][j].data === "1" ? "🟩" : board[i][j].data === "2" ? "🟥" : "⚪"}`;
}
}
returnData += leftEmoji.find(object => object.i === i).emoji + temp + "\n";
}
return returnData;
}
}
exports.DiscordBattleShip = DiscordBattleShip;
module.exports.DiscordBattleShip = DiscordBattleShip;
//# sourceMappingURL=index.js.map