Compare commits

...

17 Commits

Author SHA1 Message Date
5ab215a601 sss
All checks were successful
Deploy bot / build-and-deploy (push) Successful in 3s
2025-08-14 21:10:50 +02:00
94886ea397 update logging
All checks were successful
Deploy bot / build-and-deploy (push) Successful in 16s
2025-08-14 21:08:14 +02:00
c6c350ce0a update node
All checks were successful
Deploy bot / build-and-deploy (push) Successful in 22s
2025-08-09 00:41:37 +02:00
a3367ef2eb cigans
All checks were successful
Deploy bot / build-and-deploy (push) Successful in 13s
2025-08-09 00:39:58 +02:00
312f4f80f5 dfgfdgdfg
All checks were successful
Deploy bot / build-and-deploy (push) Successful in 15s
2025-08-09 00:38:37 +02:00
9782801da3 adjust price and regex
All checks were successful
Deploy bot / build-and-deploy (push) Successful in 14s
2025-08-08 20:19:46 +02:00
1009165751 abstract away prefix + ev cmd
All checks were successful
Deploy bot / build-and-deploy (push) Successful in 14s
2025-08-08 17:03:24 +02:00
a57aa17deb so funn, clamping
All checks were successful
Deploy bot / build-and-deploy (push) Successful in 13s
2025-08-08 13:10:50 +02:00
04e8ecc471 more more more
All checks were successful
Deploy bot / build-and-deploy (push) Successful in 13s
2025-08-08 13:03:39 +02:00
5cff410320 sss
All checks were successful
Deploy bot / build-and-deploy (push) Successful in 13s
2025-08-08 11:38:44 +02:00
61b83d6df1 upgrades people
All checks were successful
Deploy bot / build-and-deploy (push) Successful in 13s
2025-08-08 11:37:11 +02:00
e13efd0b07 add some checks, more work on price estimating
All checks were successful
Deploy bot / build-and-deploy (push) Successful in 13s
2025-08-08 02:15:58 +02:00
6b58361d31 a little more
All checks were successful
Deploy bot / build-and-deploy (push) Successful in 13s
2025-08-08 01:08:16 +02:00
45d2b2c352 xD
All checks were successful
Deploy bot / build-and-deploy (push) Successful in 14s
2025-08-08 00:29:15 +02:00
49cb0b37da bang bang
All checks were successful
Deploy bot / build-and-deploy (push) Successful in 14s
2025-08-08 00:24:06 +02:00
dfc14f7f33 more more
All checks were successful
Deploy bot / build-and-deploy (push) Successful in 14s
2025-08-07 23:56:07 +02:00
2594991b21 fix
All checks were successful
Deploy bot / build-and-deploy (push) Successful in 13s
2025-08-07 23:38:20 +02:00
17 changed files with 608 additions and 351 deletions

View File

@ -1,4 +1,4 @@
ARG NODE_VERSION=18.0.0
ARG NODE_VERSION=23.0.0
FROM node:${NODE_VERSION}-alpine

View File

@ -1,12 +1,19 @@
exports.name = "bonus";
exports.description =
":star: Sums and calculates bonus for player stat numbers.";
exports.usage = "<<bonus <number> <number> [inf optional extra numbers]";
exports.example = "<<bonus 100 100";
exports.usage =
"CLIENT_PREFIX:bonus <number> <number> [inf optional extra numbers]";
exports.example = "CLIENT_PREFIX:bonus 100 100";
exports.hidden = false;
exports.run = (client, message, args) => {
let is1nan = false;
if (!args[0])
return message.channel.send("Not enough arguments, consult <<help.");
return message.channel.send(
"Not enough arguments, consult CLIENT_PREFIX:help.".replaceAll(
"CLIENT_PREFIX:",
client.prefix
)
);
args.forEach((arg) => {
if (isNaN(parseInt(arg))) {
is1nan = true;
@ -15,7 +22,10 @@ exports.run = (client, message, args) => {
});
if (is1nan)
return message.channel.send(
"You have provided invalid arguments to this command, please consult <<help."
"You have provided invalid arguments to this command, please consult CLIENT_PREFIX:help.".replaceAll(
"CLIENT_PREFIX:",
client.prefix
)
);
let sum = 0;
args.forEach((arg) => {

View File

@ -1,15 +1,24 @@
exports.name = "cat";
exports.description = ":cat: Calculates boost for cat.";
exports.usage = "<<cat <boost> <levels>";
exports.example = "<<cat 80 120";
exports.usage = "CLIENT_PREFIX:cat <boost> <levels>";
exports.example = "CLIENT_PREFIX:cat 80 120";
exports.hidden = false;
exports.run = (client, message, args) => {
if (!args[0] || !args[1])
return message.channel.send("Not enough arguments, consult <<help.");
return message.channel.send(
"Not enough arguments, consult CLIENT_PREFIX:help.".replaceAll(
"CLIENT_PREFIX:",
client.prefix
)
);
let boost = parseInt(args[0]);
let levels = parseInt(args[1]);
if (isNaN(boost) || isNaN(levels))
return message.channel.send(
"Boost or levels is not a number. Consult <<help."
"Boost or levels is not a number. Consult CLIENT_PREFIX:help.".replaceAll(
"CLIENT_PREFIX:",
client.prefix
)
);
let extraboost = Math.floor(levels / 3 - 3);
let players = Math.floor(levels / 29) + 1;

View File

@ -1,11 +1,18 @@
const log = require("../log");
exports.name = "cb";
exports.description =
":crossed_swords: Calculate calamity blade damage taking in to account projectile speed.";
exports.usage = "<<cb <damage> <ups> [projectile speed]";
exports.example = "<<cb 10000 250 10000";
exports.usage = "CLIENT_PREFIX:cb <damage> <ups> [projectile speed]";
exports.example = "CLIENT_PREFIX:cb 10000 250 10000";
exports.hidden = false;
exports.run = (client, message, args) => {
if (!args[0] || !args[1])
return message.channel.send("Not enough arguments, consult <<help.");
return message.channel.send(
"Not enough arguments, consult CLIENT_PREFIX:help.".replaceAll(
"CLIENT_PREFIX:",
client.prefix
)
);
let cb = parseInt(args[0]);
let ups = parseInt(args[1]);
if (isNaN(cb) || isNaN(ups))
@ -17,9 +24,8 @@ exports.run = (client, message, args) => {
return message.channel.send("Projectile speed should be a number.");
initialspeed = 30000 - initialspeed;
let t = Math.ceil(initialspeed / 1200);
console.log(t);
log.info(t);
ups = ups - t;
}
console.log(ups);
message.channel.send(`Should be ${cb + ups * 192}.`);
};

212
commands/estimate.js Normal file
View File

@ -0,0 +1,212 @@
const priceTable = [
{
regex: /\b(?:thp|summoner|summ|thp armor|summoner armor|summ armor)\b/i,
returnKeyWord: "summoner piece",
prices: [
"1000;10",
"1050;12",
"1100;15",
"1150;25",
"1200;30",
"1250;100",
"1300;500",
],
},
{
regex: /\b(?:dps|ab2|dps armor|ab2 armor)\b/i,
returnKeyWord: "dps/ab2 piece",
prices: [
"1400;20",
"1450;35",
"1500;50",
"1550;75",
"1600;150",
"1650;500",
"1700;1000",
"1750;2000",
"1800;5000",
],
},
{
regex: /\b(?:tb|ab1)\b/i,
returnKeyWord: "tb/ab1 piece",
prices: ["980;5", "1050;15", "1100;40", "1150;70", "1200;500"],
},
{
regex: /\b(?:app builder armor|apprentice builder piece|app builder piece|apprentice builder armor|app piece|apprentice piece)\b/i,
returnKeyWord: "apprentice builder piece",
prices: [
"2000;25",
"2050;35",
"2100;50",
"2150;80",
"2200;150",
"2250;220",
"2300;500",
"2350;1000",
"2400;2000",
],
},
{
regex: /\b(?:app builder staff|apprentice builder staff|app staff|apprentice staff)\b/i,
returnKeyWord: "apprentice builder staff",
prices: [
"2200;5",
"2250;15",
"2300;25",
"2350;35",
"2400;45",
"2450;70",
"2500;100",
"2550;175",
"2600;250",
"2700;400",
"2800;500",
"2900;2000",
],
},
{
regex: /\b(?:aura|trange)\b/i,
returnKeyWord: "aura/trange piece",
prices: ["980;5", "1050;15", "1100;40", "1150;70", "1200;500"],
},
];
function constructPriceSubTable(idx) {
const orig = priceTable[idx];
const priceSubTable = [];
orig.prices.forEach((price) => {
const splitPrice = price.split(";");
const obj = {
number: parseInt(splitPrice[0]),
val: parseInt(splitPrice[1]),
};
priceSubTable.push(obj);
});
return priceSubTable;
}
function findClosestPrice(subtable, gameValue) {
let closest = subtable[0];
let smallestDiff = Math.abs(subtable[0].number - gameValue);
for (let i = 1; i < subtable.length; i++) {
const diff = Math.abs(subtable[i].number - gameValue);
if (diff < smallestDiff) {
smallestDiff = diff;
closest = subtable[i];
}
}
return { val: closest.val, diff: smallestDiff };
}
function weightedInterpolatePrice(pricesArr, inputNum, p = 2) {
let lower = null;
let upper = null;
for (let i = 0; i < pricesArr.length; i++) {
if (pricesArr[i].number === inputNum) return pricesArr[i].val;
if (pricesArr[i].number < inputNum) lower = pricesArr[i];
if (pricesArr[i].number > inputNum) {
upper = pricesArr[i];
break;
}
}
if (!lower) return upper.val;
if (!upper) return lower.val;
const fraction = (inputNum - lower.number) / (upper.number - lower.number);
const weight =
Math.pow(fraction, p) /
(Math.pow(fraction, p) + Math.pow(1 - fraction, p));
return lower.val + weight * (upper.val - lower.val);
}
exports.name = "estimate";
exports.description =
":money_with_wings: Give an estimated price of certain items in cv.";
exports.usage =
"CLIENT_PREFIX:estimate [summary of item you want the price of]";
exports.example =
"CLIENT_PREFIX:estimate 1600 ab2\nCLIENT_PREFIX:estimate 1100 thp\nCLIENT_PREFIX:estimate 2400 app builder staff\n(you can also include `showtable` anywhere to show raw price data)\n(always put number first, do not put conflicting data to avoid confusion)";
exports.hidden = false;
exports.run = async (client, message, args) => {
if (!args[0]) {
let tbl = "";
priceTable.forEach((p) => {
tbl += p.returnKeyWord + "\n";
});
return message.channel
.send(`Available items in pricing table:\n\`\`\`\n${tbl}\`\`\`\nFor usage, consult \`CLIENT_PREFIX:help estimate\`.replaceAll("CLIENT_PREFIX:", client.prefix)
`);
}
const combined = args.join(" ");
let entryFound = false;
let estimatedPrice = 0;
let subtable, closestInTable;
let gameValue = 0;
let returnKeyWord = "";
args.forEach((arg) => {
if (!isNaN(parseInt(arg)) && !gameValue) {
gameValue = parseInt(arg);
}
});
if (!gameValue) {
return message.channel.send(
"You didn't include any numbers in your query. Try again or consult `CLIENT_PREFIX:help estimate`.".replaceAll(
"CLIENT_PREFIX:",
client.prefix
)
);
}
priceTable.forEach((price, idx) => {
if (price.regex.test(combined) && !entryFound) {
entryFound = true;
returnKeyWord = price.returnKeyWord;
subtable = constructPriceSubTable(idx);
closestInTable = findClosestPrice(subtable, gameValue);
estimatedPrice = Math.round(
weightedInterpolatePrice(subtable, gameValue)
);
if (idx == 0 && closestInTable.diff > 100) {
estimatedPrice = 0;
}
if (estimatedPrice > 100000) {
estimatedPrice = 100000;
}
}
});
if (entryFound) {
if (combined.includes("showtable")) {
let res = "";
subtable.forEach((entry) => {
res += String(entry.number).padEnd(14, " ") + entry.val + "\n";
});
message.channel.send(
`Displaying data price table for \`${returnKeyWord}\`.\n\`\`\`\nGame Value\tPrice\n${res}\`\`\``
);
} else {
let stringprice;
if (estimatedPrice > 499) {
stringprice = `**__${estimatedPrice}__** (atleast 1 cheater, worth auctioning)`;
} else if (estimatedPrice == 0) {
stringprice = "**0** (<:TavKeep:1179145911180480563>)";
} else {
stringprice = `**${estimatedPrice}**`;
}
message.channel.send(
`Hmm... I estimate your **${gameValue} ${returnKeyWord}** to be worth approximately ${stringprice} cv.\n*Estimation is provided through looking at past trades/price checks/sheets in DDRNG.*\n*Closest price in table: **${closestInTable.val}** cv, diff: **${closestInTable.diff}***.`
);
}
} else
message.channel.send(
"Your query couldn't be matched with anything, please refine your query or reffer to `CLIENT_PREFIX:help estimate` or use `CLIENT_PREFIX:estimate` to see all available price tables for more information.".replaceAll(
"CLIENT_PREFIX:",
client.prefix
)
);
};

113
commands/ev.js Normal file
View File

@ -0,0 +1,113 @@
const BASEDMG = 1e9; // absurdly high to avoid base damage bottleneck
function getHeroDamageScaling(statVal) {
return (
1.0 +
0.33 *
(Math.pow(4.0, 0.1 * 1.1) -
1.0 +
0.68 * (Math.pow(statVal + 1, 0.375 * 1.1) - 1.0))
);
}
function getAb2Scaling(statVal) {
return (
1.0 +
0.66 * (Math.pow(4.0, 0.0825) - 1.0) +
0.75 * (Math.pow(statVal + 1, 0.3375) - 1.0)
);
}
function getTotalDamage(hdmgStat, baseDamage) {
return Math.max(
Math.max(32.0, Math.max(1.0, baseDamage)) *
getHeroDamageScaling(hdmgStat) *
(1.2 * 0.8 * 0.155),
1.0
);
}
function getBeamDamage(hdmg, ab2) {
return (
getTotalDamage(hdmg, BASEDMG) * 0.6 * Math.pow(getAb2Scaling(ab2), 0.93)
);
}
/**
* Find optimal split of totalStats between hdmg and ab2
* using a binary/golden section search.
*/
function findOptimalDistribution(totalStats) {
let left = 0;
let right = totalStats;
const phi = (Math.sqrt(5) - 1) / 2; // golden ratio for faster convergence
let x1 = right - phi * (right - left);
let x2 = left + phi * (right - left);
while (Math.abs(right - left) > 1e-3) {
const f1 = getBeamDamage(x1, totalStats - x1);
const f2 = getBeamDamage(x2, totalStats - x2);
if (f1 < f2) {
left = x1;
x1 = x2;
x2 = left + phi * (right - left);
} else {
right = x2;
x2 = x1;
x1 = right - phi * (right - left);
}
}
const hdmg = (left + right) / 2;
const ab2 = totalStats - hdmg;
return {
hdmg: hdmg,
ab2: ab2,
};
}
exports.name = "ev";
exports.description =
":robot: Calculate how much hero damage/ab2 you should aim for.";
exports.usage =
"CLIENT_PREFIX:ev <hdmg> <ab2> OR CLIENT_PREFIX:ev <total stats>";
exports.example = "CLIENT_PREFIX:ev 6000 5000\nCLIENT_PREFIX:ev 10000";
exports.hidden = false;
exports.run = (client, message, args) => {
if (!args[0])
return message.channel.send(
"Not enough arguments, consult CLIENT_PREFIX:help.".replaceAll(
"CLIENT_PREFIX:",
client.prefix
)
);
let total = parseInt(args[0]);
if (isNaN(total))
return message.channel.send(
"One of the arguments is not a number. Consult CLIENT_PREFIX:help.".replaceAll(
"CLIENT_PREFIX:",
client.prefix
)
);
if (args[1]) {
if (isNaN(parseInt(args[1]))) {
return message.channel.send(
"One of the arguments is not a number. Consult CLIENT_PREFIX:help.".replaceAll(
"CLIENT_PREFIX:",
client.prefix
)
);
} else total += parseInt(args[1]);
}
const distribution = findOptimalDistribution(total);
message.channel.send(
`You should aim for **${
Math.round(distribution.hdmg) - 3
}** hero damage and **${
Math.round(distribution.ab2) + 3
}** ab2. *(approximately)*`
);
};

55
commands/hb.js Normal file
View File

@ -0,0 +1,55 @@
const STAT_MULT_INITIAL_AB2 = 0.66;
const STAT_MULT_FULL_AB2 = 0.75;
const STAT_EXP_INITIAL_AB2 = 0.0825;
const STAT_EXP_FULL_AB2 = 0.3375;
const ADDITIONAL_DAMAGE_MULTIPLIER = 0.25;
const ADDITIONAL_DAMAGE_EXPONENT = 0.95;
// Functions
function get_ab2_scaling(stat_val) {
return (
1.0 +
STAT_MULT_INITIAL_AB2 *
(Math.min(stat_val + 1.0, 4.0) ** STAT_EXP_INITIAL_AB2 - 1.0) +
STAT_MULT_FULL_AB2 * ((stat_val + 1.0) ** STAT_EXP_FULL_AB2 - 1.0)
);
}
function get_damage_multiplier(stat_val) {
return (
1.0 +
ADDITIONAL_DAMAGE_MULTIPLIER *
get_ab2_scaling(Math.max(stat_val, 1)) ** ADDITIONAL_DAMAGE_EXPONENT
);
}
exports.name = "hb";
exports.description = ":muscle: Calculate hero boost's damage multiplier.";
exports.usage = "CLIENT_PREFIX:hb <points>";
exports.example = "CLIENT_PREFIX:hb 5000";
exports.hidden = false;
exports.run = (client, message, args) => {
if (!args[0])
return message.channel.send(
"Not enough arguments, consult CLIENT_PREFIX:help.".replaceAll(
"CLIENT_PREFIX:",
client.prefix
)
);
let hbPoints = parseInt(args[0]);
if (isNaN(hbPoints))
return message.channel.send(
"Given argument is not a number. Consult CLIENT_PREFIX:help.".replaceAll(
"CLIENT_PREFIX:",
client.prefix
)
);
message.channel.send(
`With **${hbPoints}** points in your hero boost, you will boost with a **${get_damage_multiplier(
hbPoints
).toFixed(4)}** damage multiplier.`
);
};

View File

@ -1,9 +1,10 @@
const { EmbedBuilder } = require("discord.js");
exports.name = "help";
exports.description =
":scroll: Shows this message. (this bot is a crude imitation of tbolt's better bot :P)";
exports.usage = "<<help [optional other command]";
exports.example = "<<help res";
":scroll: Shows this message and provides insight into other commands.";
exports.usage = "CLIENT_PREFIX:help [optional other command]";
exports.example = "CLIENT_PREFIX:help res";
exports.hidden = false;
exports.run = (client, message, args) => {
let embed = new EmbedBuilder();
embed.setTitle("Commands Helper");
@ -11,27 +12,37 @@ exports.run = (client, message, args) => {
const cmd = client.commands.get(args[0]);
if (!cmd)
return message.channel.send(
"Command not found, try `<<help` first."
"Command not found, try `CLIENT_PREFIX:help` first.".replaceAll(
"CLIENT_PREFIX:",
client.prefix
)
);
if (cmd.hidden && message.author.id != client.ownerID) return;
embed.setTitle(`Help for \`${cmd.name}\` command`);
embed.setDescription(cmd.description);
embed.addFields(
{
name: "Usage",
value: cmd.usage,
value: cmd.usage.replaceAll("CLIENT_PREFIX:", client.prefix),
},
{
name: "Example",
value: cmd.example,
value: cmd.example.replaceAll("CLIENT_PREFIX:", client.prefix),
}
);
} else {
client.commands.forEach((cmd) => {
embed.addFields({
name: cmd.name,
value: cmd.description,
[...client.commands.values()]
.sort((a, b) => (a.name > b.name ? 1 : -1))
.forEach((cmd) => {
if (cmd.hidden && message.author.id != client.ownerID) return;
embed.addFields({
name: cmd.name,
value: cmd.description.replaceAll(
"CLIENT_PREFIX:",
client.prefix
),
});
});
});
}
embed.setColor(0x00ff00);
message.channel.send({ embeds: [embed] });

View File

@ -1,11 +1,18 @@
const log = require("../log");
exports.name = "ms";
exports.description =
":tada: Calculate moon staff damage taking in to account projectile speed.";
exports.usage = "<<ms <elemental damage> <ups> [projectile speed]";
exports.example = "<<ms 10000 250 10000";
exports.usage = "CLIENT_PREFIX:ms <elemental damage> <ups> [projectile speed]";
exports.example = "CLIENT_PREFIX:ms 10000 250 10000";
exports.hidden = false;
exports.run = (client, message, args) => {
if (!args[0] || !args[1])
return message.channel.send("Not enough arguments, consult <<help.");
return message.channel.send(
"Not enough arguments, consult CLIENT_PREFIX:help.".replaceAll(
"CLIENT_PREFIX:",
client.prefix
)
);
let moonstaff = parseInt(args[0]);
let ups = parseInt(args[1]);
if (isNaN(moonstaff) || isNaN(ups))
@ -17,9 +24,8 @@ exports.run = (client, message, args) => {
return message.channel.send("Projectile speed should be a number.");
initialspeed = 30000 - initialspeed;
let t = Math.ceil(initialspeed / 1200);
console.log(t);
log.info(t);
ups = ups - t;
}
console.log(ups);
message.channel.send(`Should be ${moonstaff + ups * 99}.`);
};

27
commands/ping.js Normal file
View File

@ -0,0 +1,27 @@
function format(seconds) {
function pad(s) {
return (s < 10 ? "0" : "") + s;
}
var hours = Math.floor(seconds / (60 * 60));
var minutes = Math.floor((seconds % (60 * 60)) / 60);
var seconds = Math.floor(seconds % 60);
return pad(hours) + "h " + pad(minutes) + "m " + pad(seconds) + "s.";
}
exports.name = "ping";
exports.description = ":ping_pong: View service statistics.";
exports.usage = "CLIENT_PREFIX:ping";
exports.example = "CLIENT_PREFIX:ping";
exports.hidden = true;
exports.run = async (client, message, args) => {
message.channel.send("Fetching data...").then((m) => {
m.edit(
`🏓 Latency is **${
m.createdTimestamp - message.createdTimestamp
}ms**.\nAPI Latency is **${Math.round(
client.ws.ping
)}ms**.\nClient uptime is **${format(process.uptime())}**`
);
});
};

25
commands/refetch.js Normal file
View File

@ -0,0 +1,25 @@
exports.name = "refetch";
exports.description = ":ninja: Refetch user react data.";
exports.usage = "CLIENT_PREFIX:refetch";
exports.example = "CLIENT_PREFIX:refetch";
exports.hidden = true;
exports.run = async (client, message, args) => {
if (message.author.id != client.ownerID) return;
try {
const url = "https://drive.overflow.fun/public/react.json";
client.usersToReactTo = [];
const res = await fetch(url);
const data = await res.json();
client.usersToReactTo = data.map((entry) => {
const [userId, emoji] = entry.split("|");
return { userId, emoji };
});
message.channel.send(
"Refetched react data. " + JSON.stringify(client.usersToReactTo)
);
} catch (e) {
message.channel.send("copyparty instance unreachable/offline...");
}
};

View File

@ -2,8 +2,9 @@ exports.name = "res";
exports.description =
":shield: Calculates how much upgrades you need to max your resistances. For Ult pieces and better. (slightly inaccurate but this is hard to calculate)";
exports.usage =
"<<res <res> <res> <res> [res] | <hero stat> <levels> [second hero stat]";
exports.example = "<<res -3 1 -16 14 | 406 440";
"CLIENT_PREFIX:res <res> <res> <res> [res] | <hero stat> <levels> [second hero stat]";
exports.example = "CLIENT_PREFIX:res -3 1 -16 14 | 406 440";
exports.hidden = false;
exports.run = (client, message, args) => {
for (let i = 0; i < args.length; i++) {
if (args[i] == "|") continue;

56
commands/tb.js Normal file
View File

@ -0,0 +1,56 @@
const STAT_MULT_INITIAL_AB2 = 0.66;
const STAT_MULT_FULL_AB2 = 0.75;
const STAT_EXP_INITIAL_AB2 = 0.0825;
const STAT_EXP_FULL_AB2 = 0.3375;
// only this changed for tower boosting
const ADDITIONAL_DAMAGE_MULTIPLIER = 0.1;
const ADDITIONAL_DAMAGE_EXPONENT = 1.25;
// Functions
function get_ab2_scaling(stat_val) {
return (
1.0 +
STAT_MULT_INITIAL_AB2 *
(Math.min(stat_val + 1.0, 4.0) ** STAT_EXP_INITIAL_AB2 - 1.0) +
STAT_MULT_FULL_AB2 * ((stat_val + 1.0) ** STAT_EXP_FULL_AB2 - 1.0)
);
}
function get_damage_multiplier(stat_val) {
return (
1.0 +
ADDITIONAL_DAMAGE_MULTIPLIER *
get_ab2_scaling(Math.max(stat_val, 1)) ** ADDITIONAL_DAMAGE_EXPONENT
);
}
exports.name = "tb";
exports.description = ":fire: Calculate tower boost's damage multiplier.";
exports.usage = "CLIENT_PREFIX:tb <points>";
exports.example = "CLIENT_PREFIX:tb 5000";
exports.hidden = false;
exports.run = (client, message, args) => {
if (!args[0])
return message.channel.send(
"Not enough arguments, consult CLIENT_PREFIX:help.".replaceAll(
"CLIENT_PREFIX:",
client.prefix
)
);
let tbPoints = parseInt(args[0]);
if (isNaN(tbPoints))
return message.channel.send(
"Given argument is not a number. Consult CLIENT_PREFIX:help.".replaceAll(
"CLIENT_PREFIX:",
client.prefix
)
);
message.channel.send(
`With **${tbPoints}** points in your tower boost, you will boost towers with a **${get_damage_multiplier(
tbPoints
).toFixed(4)}** multiplier.`
);
};

View File

@ -1,6 +1,6 @@
services:
shirocalc:
container_name: shirocalc-discord-bot
container_name: shirocalc
restart: unless-stopped
environment:
- TOKEN=${TOKEN}

View File

@ -1,6 +1,6 @@
const { Client, Events, GatewayIntentBits, Collection } = require("discord.js");
const fs = require("fs");
const path = require("path");
const log = require("./log");
let cfg = {};
try {
cfg = require("./config.json");
@ -19,6 +19,8 @@ const client = new Client({
],
});
client.commands = new Collection();
client.ownerID = 263247134147608578;
client.prefix = prefix;
fs.readdir("./commands/", (err, files) => {
if (err) return console.error(err);
@ -26,33 +28,40 @@ fs.readdir("./commands/", (err, files) => {
if (!file.endsWith(".js")) return;
let props = require(`./commands/${file}`);
let commandName = file.split(".")[0];
console.log("Loaded command " + commandName);
log.info("Loaded command " + commandName);
client.commands.set(commandName, props);
});
});
const url = "https://drive.overflow.fun/public/react.json";
let userstoreact = [];
client.usersToReactTo = [];
client.once(Events.ClientReady, async (readyClient) => {
const res = await fetch(url);
const data = await res.json();
try {
const res = await fetch(url);
const data = await res.json();
userstoreact = data.map((entry) => {
const [userId, emoji] = entry.split(":");
return { userId, emoji };
});
console.log(`Ready! Logged in as ${readyClient.user.tag}`);
client.usersToReactTo = data.map((entry) => {
const [userId, emoji] = entry.split("|");
return { userId, emoji };
});
} catch (e) {
console.warn("copyparty is offline, wont work.");
}
log.info(`Ready! Logged in as ${readyClient.user.tag}`);
});
client.on(Events.MessageCreate, async (message) => {
if (message.author.bot) return;
console.log(message.mentions.users);
userstoreact.forEach(async (usr) => {
if (message.mentions.users.find((u) => u.id == usr.userId)) {
client.usersToReactTo.forEach(async (item) => {
if (
message.mentions.users.find((u) => u.id == item.userId) &&
!message.reference
) {
try {
await message.react(emoji);
await message.react(item.emoji);
} catch (err) {
message.channel.send("FAILED TO REACT TO MESSAGE! " + err);
console.error("Failed to react to message:", err);
}
}
@ -66,8 +75,13 @@ client.on(Events.MessageCreate, async (message) => {
const cmd = client.commands.get(command);
if (!cmd) return;
cmd.run(client, message, args);
try {
await cmd.run(client, message, args);
} catch (e) {
message.channel.send(
"Command " + cmd.name + " exited with an exception: " + e
);
}
});
// Log in to Discord with your client's token

14
log.js Normal file
View File

@ -0,0 +1,14 @@
// logger.js
function log(level, message, ...args) {
const now = new Date().toISOString();
const levelLabel = `[${level.toUpperCase()}]`.padEnd(8);
console.log(`${now} ${levelLabel} ${message}`, ...args);
}
module.exports = {
info: (msg, ...args) => log("info", msg, ...args),
warn: (msg, ...args) => log("warn", msg, ...args),
error: (msg, ...args) => log("error", msg, ...args),
debug: (msg, ...args) => log("debug", msg, ...args),
};

302
old.js
View File

@ -1,302 +0,0 @@
const {
Client,
Events,
GatewayIntentBits,
EmbedBuilder,
} = require("discord.js");
let cfg = require("./config.json");
const token = process.env.TOKEN || cfg.TOKEN;
const prefix = process.env.PREFIX || cfg.PREFIX;
const client = new Client({
intents: [
GatewayIntentBits.Guilds,
GatewayIntentBits.GuildMessages,
GatewayIntentBits.MessageContent,
],
});
client.once(Events.ClientReady, (readyClient) => {
console.log(`Ready! Logged in as ${readyClient.user.tag}`);
});
client.on(Events.MessageCreate, (message) => {
if (message.author.bot) return;
if (message.content.indexOf(prefix) !== 0) return;
const args = message.content.slice(prefix.length).trim().split(/ +/g);
const command = args.shift().toLowerCase();
if (command == "ev") {
return message.channel.send("not implemented.");
if (!args[0] || !args[1])
return message.channel.send(
"Not enough arguments, consult <<help."
);
let ab1 = parseInt(args[0]);
let ab2 = parseInt(args[1]);
let ab1ratio = 1.063;
let ab2ratio = 0.937;
message.channel.send(
`Aiming for ${ab1 * ab1ratio} and ${ab2 * ab2ratio}`
);
}
if (command == "lt") {
return message.channel.send("not implemented.");
if (!args[0] || !args[1])
return message.channel.send(
"Not enough arguments, consult <<help."
);
let ab1 = parseInt(args[0]);
let ab2 = parseInt(args[1]);
let ab1ratio = 1.101;
let ab2ratio = 0.899;
message.channel.send(
`Aiming for ${ab1 * ab1ratio} and ${ab2 * ab2ratio}`
);
}
if (command == "cb") {
}
if (command == "ms") {
if (!args[0] || !args[1])
return message.channel.send(
"Not enough arguments, consult <<help."
);
let cb = parseInt(args[0]);
let ups = parseInt(args[1]);
let initialspeed = null;
if (args[2]) {
initialspeed = parseInt(args[2]);
initialspeed = 30000 - initialspeed;
let t = Math.ceil(initialspeed / 1200);
console.log(t);
ups = ups - t;
}
console.log(ups);
message.channel.send(`Should be ${cb + ups * 99}.`);
}
if (command == "res") {
for (let i = 0; i < args.length; i++) {
if (args[i] == "|") continue;
args[i] = parseInt(args[i]);
}
let type;
if (args[4] == "|") type = 4;
else if (args[3] == "|") type = 3;
else return message.channel.send("Please provide 3 or 4 resistances.");
let resistances = [
args[0],
args[1],
args[2],
type == 4 ? args[3] : null,
];
let mainStat = type == 4 ? args[5] : args[4];
let upgrades = type == 4 ? args[6] : args[5];
let substat = type == 4 ? args[7] : args[6];
upgrades = upgrades - 1;
if (!mainStat || !upgrades)
return message.channel.send("Please atleast 2 stats.");
if (!substat) substat = 0;
let resUpgrades = 0;
resistances.forEach((res) => {
if (res == null) return;
if (res > 29)
return message.channel.send(
"Please provide a number between 0 and 29. LMAO"
);
if (res < 29) {
if (res < 0) {
let underzero = Math.abs(res);
if (underzero < 13) {
resUpgrades += 23 + Math.abs(res);
} else {
if (underzero == 13 || underzero == 14) {
resUpgrades += 23 + 13;
}
if (underzero == 15 || underzero == 16) {
resUpgrades += 23 + 13 + 1;
}
if (underzero == 17 || underzero == 18) {
resUpgrades += 23 + 13 + 2;
}
if (
underzero == 19 ||
underzero == 20 ||
underzero == 22
) {
resUpgrades += 23 + 13 + 3;
}
if (underzero == 21 || underzero == 23) {
resUpgrades += 23 + 13 + 4;
}
if (underzero > 23) {
resUpgrades += 29 - underzero;
}
}
// from 1% to 23% takes 17-18 ups
// from 23% to 29% takes 6 ups
// from 1% to 29% takes 23 ups
// 23 + Math.abs(res)
console.log(
"gotta spend " +
(23 + Math.abs(res)) +
" upgrades to max res for negative"
);
} else {
//14 skips 15 to 16
//15 skips 16 to 17
//17 skips 18 to 19
//19 skips 20 to 21
//21 skips to 24
// list: 1,2,3,4,5,6,7,8,9,10,11,12,13,14,16,18,20,23
// possible if 15,17,19,21,24
//amount of numbers: 18
// skipped 5 upgrades
//example 4
// 5,6,7,8,9,10,11,12,13,14,16,18,20,23
// count: 14
// to 29% + 6, result 20
if (res < 14) {
let am = 14 - res;
resUpgrades += am + 4 + 6;
console.log(
"gotta spend " +
(am + 4 + 6) +
" upgrades to max res for positive, am: " +
am
);
} else {
if (res == 14) {
resUpgrades += 4 + 6;
}
if (res == 15 || res == 16) {
resUpgrades += 3 + 6;
}
if (res == 17 || res == 18) {
resUpgrades += 2 + 6;
}
if (res == 19 || res == 20 || res == 22) {
resUpgrades += 1 + 6;
}
if (res == 21 || res == 23) {
resUpgrades += 6;
}
if (res > 23) {
resUpgrades += 29 - res;
}
console.log(
"gotta spend " +
resUpgrades +
" upgrades to max res for positive, res: " +
res
);
}
}
}
});
upgrades -= resUpgrades;
mainStat += upgrades;
let over = 0;
if (mainStat > 999) {
let over = mainStat - 999;
mainStat -= over;
substat += over;
}
message.channel.send(
`With ${resUpgrades} upgrades spent in resistances, your piece will reach ${
mainStat + substat
}, or ${Math.ceil(mainStat * 1.4 + substat * 1.4)} with set bonus!`
);
}
if (command == "cat") {
if (!args[0] || !args[1])
return message.channel.send(
"Not enough arguments, consult <<help."
);
let boost = parseInt(args[0]);
let levels = parseInt(args[1]);
let extraboost = Math.floor(levels / 3 - 3);
let players = Math.floor(levels / 29) + 1;
if (players > 4) players = 4;
message.channel.send(
`Should be atleast ${
boost + extraboost
}. Will boost atleast ${players} players.`
);
}
if (command == "bident") {
if (!args[0] || !args[1])
return message.channel.send(
"Not enough arguments, consult <<help."
);
let damage = parseInt(args[0]);
let ups = parseInt(args[1]);
let initialcharge = null;
let initialspeed = null;
if (args[2]) {
initialspeed = parseInt(args[2]);
initialspeed = 30000 - initialspeed;
let t = Math.ceil(initialspeed / 1200);
console.log(t);
ups = ups - t;
}
if (args[3]) {
initialcharge = parseInt(args[3]);
initialcharge = 128 - initialcharge;
let t = Math.ceil(initialcharge / 4);
console.log(t);
ups = ups - t;
}
message.channel.send(`Should be ${damage + ups * 144}.`);
}
if (command == "cdrag") {
if (!args[0] || !args[1] || !args[2])
return message.channel.send(
"Not enough arguments, consult <<help."
);
let damage = parseInt(args[0]);
let upgrades = parseInt(args[1]);
let additional = parseInt(args[2]);
if (additional < 3) upgrades = upgrades - (3 - additional);
message.channel.send(
`Should be ${
damage + upgrades * 112
} damage with maxed additional projectiles.`
);
}
if (command == "help") {
let embed = new EmbedBuilder();
embed.setTitle("Commands");
if (prefix == "?")
embed.setDescription("**development build, subject to change**");
embed.addFields(
{
name: "cat",
value: ":cat: Calculates boost for cat. (<<cat <boost> <levels>)",
},
{
name: "cdrag",
value: ":dragon: Calculate damage for crystaline dragon taking into account upgrades and additional projectiles. (<<cdrag <base> <ups> <extra projectiles>)",
},
{
name: "cb",
value: ":crossed_swords: Calculate calamity blade damage taking in to account speed. (<<cb <damage> <ups> [proj speed])",
},
{
name: "bident",
value: ":trident: Calculate bident damage taking in to account speed and charge speed. (<<bident <damage> <ups> [proj speed] [charge speed])",
},
{
name: "ms",
value: "",
},
{
name: "help",
value: ":scroll: Shows this message. (bot is a tbot replacement for this server :P)",
}
);
embed.setColor(0x00ff00);
message.channel.send({ embeds: [embed] });
}
});
// Log in to Discord with your client's token
client.login(token);