From f44f79a955ec687d2ad3744b9b73b23d269d2793 Mon Sep 17 00:00:00 2001 From: "Jill \"oatmealine\" Monoids" Date: Tue, 21 Nov 2023 23:28:32 +0300 Subject: [PATCH] pvp cooldowns --- migrations/20231121194328_invincible.js | 20 +++++++++++++++ src/commands/attack.ts | 9 ++++--- src/commands/craft.ts | 3 ++- src/lib/db.ts | 4 +++ src/lib/rpg/counter.ts | 2 ++ src/lib/rpg/pvp.ts | 33 ++++++++++++++++++++++++- 6 files changed, 66 insertions(+), 5 deletions(-) create mode 100644 migrations/20231121194328_invincible.js diff --git a/migrations/20231121194328_invincible.js b/migrations/20231121194328_invincible.js new file mode 100644 index 0000000..a672087 --- /dev/null +++ b/migrations/20231121194328_invincible.js @@ -0,0 +1,20 @@ +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ +exports.up = function(knex) { + return knex.schema + .createTable('invincibleUsers', table => { + table.string('user').notNullable().unique(); + table.timestamp('since').notNullable(); + }); +}; + +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ +exports.down = function(knex) { + return knex.schema + .dropTable('invincibleUsers'); +}; diff --git a/src/commands/attack.ts b/src/commands/attack.ts index b429ea4..0dad3a4 100644 --- a/src/commands/attack.ts +++ b/src/commands/attack.ts @@ -1,7 +1,7 @@ import { GuildMember, CommandInteraction, SlashCommandBuilder } from 'discord.js'; -import { weaponAutocomplete, getItem, getItemQuantity, formatItems } from '../lib/rpg/items'; +import { weaponAutocomplete, getItem, getItemQuantity, formatItems, formatItem } from '../lib/rpg/items'; import { Command } from '../types/index'; -import { initHealth, dealDamage, BLOOD_ITEM, BLOOD_ID } from '../lib/rpg/pvp'; +import { initHealth, dealDamage, BLOOD_ITEM, BLOOD_ID, resetInvincible, INVINCIBLE_TIMER, getInvincibleMs } from '../lib/rpg/pvp'; export default { data: new SlashCommandBuilder() @@ -35,12 +35,15 @@ export default { const weapon = await getItem(weaponID); if (!weapon) return interaction.followUp('No such item exists!'); if (weapon.type !== 'weapon') return interaction.followUp('That is not a weapon!'); + const invinTimer = await getInvincibleMs(user.id); + if (invinTimer > 0) return interaction.followUp(`You can only attack this user (or if they perform an action first)!`); const dmg = weapon.maxStack; await dealDamage(user.id, dmg); const newHealth = await getItemQuantity(user.id, BLOOD_ID); - await interaction.followUp(`You hit ${user} for ${BLOOD_ITEM.emoji} **${dmg}** damage! They are now at ${formatItems(BLOOD_ITEM, newHealth.quantity)}.`); + if (user.id !== member.id) await resetInvincible(member.id); + await interaction.followUp(`You hit ${user} with ${formatItem(weapon)} for ${BLOOD_ITEM.emoji} **${dmg}** damage! They are now at ${formatItems(BLOOD_ITEM, newHealth.quantity)}.\nYou can attack them again (or if they perform an action first).`); }, autocomplete: weaponAutocomplete, diff --git a/src/commands/craft.ts b/src/commands/craft.ts index ae7b910..a0d3d4f 100644 --- a/src/commands/craft.ts +++ b/src/commands/craft.ts @@ -4,7 +4,7 @@ import { getStation, canUseStation, craftingStations, verb, CraftingStation } fr import { formatItem, getItemQuantity, formatItems, getMaxStack, giveItem, formatItemsArray } from '../lib/rpg/items'; import { getRecipe, defaultRecipes, formatRecipe, resolveCustomRecipe } from '../lib/rpg/recipes'; import { Command } from '../types/index'; -import { initHealth } from '../lib/rpg/pvp'; +import { initHealth, resetInvincible } from '../lib/rpg/pvp'; export default { data: new SlashCommandBuilder() @@ -98,6 +98,7 @@ export default { nextUsableAt = Date.now() + station.cooldown * 1000; } + await resetInvincible(member.id); return interaction.followUp(`${station.emoji} ${verb(station)} ${formatItemsArray(outputs)}!${outputs.length === 1 ? `\n_${outputs[0].item.description}_` : ''}${nextUsableAt ? `\n${station.name} usable again ` : ''}`); }, diff --git a/src/lib/db.ts b/src/lib/db.ts index 7616aa7..812994c 100644 --- a/src/lib/db.ts +++ b/src/lib/db.ts @@ -89,4 +89,8 @@ export interface Session { } export interface InitHealth { user: string, +} +export interface InvincibleUser { + user: string, + since: number, } \ No newline at end of file diff --git a/src/lib/rpg/counter.ts b/src/lib/rpg/counter.ts index c9e0c39..9ec814f 100644 --- a/src/lib/rpg/counter.ts +++ b/src/lib/rpg/counter.ts @@ -2,6 +2,7 @@ import { Client, CommandInteraction, GuildMember, EmbedBuilder, TextChannel, Aut import { getSign } from '../util'; import { Counter, CounterConfiguration, CounterUserLink, db } from '../db'; import { formatItems, getItem, getItemQuantity, getMaxStack, giveItem } from './items'; +import { resetInvincible } from './pvp'; export async function getCounter(id: number) { const counter = await db('counters') @@ -359,6 +360,7 @@ function changeCounterInteractionBuilder(linked: boolean) { newInv = await giveItem(member.id, item, amtInv); } + await resetInvincible(member.id); const newCount = await changeCounter(counter.id, amount); await updateCounter(interaction.client, counter, newCount); await announceCounterUpdate(interaction.client, member, amount, counter, newCount, linked); diff --git a/src/lib/rpg/pvp.ts b/src/lib/rpg/pvp.ts index f7a047b..7bc89d5 100644 --- a/src/lib/rpg/pvp.ts +++ b/src/lib/rpg/pvp.ts @@ -1,4 +1,4 @@ -import { InitHealth, ItemInventory, db } from '../db'; +import { InitHealth, InvincibleUser, ItemInventory, db } from '../db'; import { DefaultItems, getDefaultItem, giveItem, getItemQuantity, formatItems } from './items'; import { Client } from 'discord.js'; @@ -6,6 +6,7 @@ export const BLOOD_ID = -DefaultItems.BLOOD; export const BLOOD_ITEM = getDefaultItem(BLOOD_ID); export const MAX_HEALTH = BLOOD_ITEM.maxStack; const BLOOD_GAIN_PER_HOUR = 5; +export const INVINCIBLE_TIMER = 1_000 * 60 * 60; export async function initHealth(user: string) { const isInitialized = await db('initHealth') @@ -18,6 +19,35 @@ export async function initHealth(user: string) { } } +export async function getInvincibleMs(user: string) { + const invincible = await db('invincibleUsers') + .where('user', user) + .first(); + + if (!invincible) return 0; + return Math.max((invincible.since + INVINCIBLE_TIMER) - Date.now(), 0); +} + +export async function resetInvincible(user: string) { + await db('invincibleUsers') + .where('user', user) + .delete(); +} + +export async function applyInvincible(user: string) { + const exists = await db('invincibleUsers') + .where('user', user) + .first(); + + if (exists) { + await db('invincibleUsers') + .update({ since: Date.now() }); + } else { + await db('invincibleUsers') + .insert({ since: Date.now(), user }); + } +} + export async function getHealth(user: string) { await initHealth(user); return await getItemQuantity(user, BLOOD_ID); @@ -25,6 +55,7 @@ export async function getHealth(user: string) { export async function dealDamage(user: string, dmg: number) { await initHealth(user); + await applyInvincible(user); return await giveItem(user, BLOOD_ITEM, -dmg); }