mirror of
https://github.com/qwerinope/qweribot.git
synced 2025-12-18 15:01:38 +01:00
add albeees style random bullet shooting
This commit is contained in:
@@ -204,7 +204,7 @@ NAME|COMMAND|FUNCTION|ALIASES|COST
|
||||
-|-|-|-|-
|
||||
Blaster|`blaster {target}`|Times targeted user out for 60 seconds|`blaster` `blast`|100
|
||||
Grenade|`grenade`|Times a random vulnerable chatter out for 60 seconds|`grenade`|99
|
||||
Silver Bullet|`silverbullet {target}`|Times targeted user out for 30 minutes|`silverbullet` `execute` `{blastin}`|666
|
||||
Silver Bullet|`silverbullet {target}`|Times targeted or random vulnerable user out for 30 minutes|`silverbullet` `execute` `{blastin}`|666
|
||||
TNT|`tnt`|Give 5-10 random chatters 60 second timeouts|`tnt`|1000
|
||||
|
||||
## Cheers
|
||||
@@ -214,7 +214,7 @@ NAME|AMOUNT|USAGE|FUNCTION
|
||||
`grenade`|99|`cheer99`|Times a random vulnerable chatter out for 60 seconds. Of failure gives cheerer a grenade
|
||||
`timeout`|100|`cheer100 {target}`|Times specified user out for 1 minute. On failure gives cheerer a blaster
|
||||
`superloot`|150|`cheer150`|Get superloot. Details and drop rates can be found [(here)](#lootbox).
|
||||
`execute`|666|`cheer666 {target}`|Times specified user out for 30 minutes. On failure gives cheerer a silver bullet
|
||||
`execute`|666|`cheer666 [target]`|Times specified or random vulnerable user out for 30 minutes. On failure gives cheerer a silver bullet
|
||||
`tnt`|1000|`cheer1000`|Gives 5-10 random vulnerable chatters 60 second timeouts. On failure gives cheerer a TNT
|
||||
|
||||
## Point Redeems
|
||||
|
||||
@@ -7,13 +7,35 @@ import { createTimeoutRecord } from "db/dbTimeouts";
|
||||
import { parseCheerArgs } from "lib/parseCommandArgs";
|
||||
import { createCheerEventRecord } from "db/dbCheerEvents";
|
||||
import { playAlert } from "web/alerts/serverFunctions";
|
||||
import { redis } from "lib/redis";
|
||||
|
||||
const ITEMNAME = 'silverbullet';
|
||||
|
||||
export default new Cheer('execute', 666, async (msg, user) => {
|
||||
const args = parseCheerArgs(msg.messageText);
|
||||
if (!args[0]) { await handleNoTarget(msg, user, ITEMNAME, false); return; };
|
||||
const target = await User.initUsername(args[0].toLowerCase());
|
||||
|
||||
let target: User | null;
|
||||
if (!args[0]) {
|
||||
const vulnsids = await redis.keys('user:*:vulnerable');
|
||||
const baseusers = vulnsids.map(a => User.initUserId(a.slice(5, -11)));
|
||||
const users: User[] = [];
|
||||
for (const user of baseusers) {
|
||||
const a = await user;
|
||||
if (!a) continue;
|
||||
users.push(a);
|
||||
};
|
||||
if (users.length === 0) { await sendMessage('No vulnerable chatters'); await handleNoTarget(msg, user, ITEMNAME, true); return; };
|
||||
target = users[Math.floor(Math.random() * users.length)]!;
|
||||
await playAlert({
|
||||
name: 'blastinRoulette',
|
||||
user: user.displayName,
|
||||
targets: users.map(a => a.displayName),
|
||||
finaltarget: target.displayName
|
||||
});
|
||||
await new Promise((res, _) => setTimeout(res, 6000));
|
||||
} else {
|
||||
target = await User.initUsername(args[0].toLowerCase());
|
||||
};
|
||||
if (!target) { await handleNoTarget(msg, user, ITEMNAME, false); return; };
|
||||
await getUserRecord(target);
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import parseCommandArgs from "lib/parseCommandArgs";
|
||||
import { timeout } from "lib/timeout";
|
||||
import { playAlert } from "web/alerts/serverFunctions";
|
||||
import User from "user";
|
||||
import { redis } from "lib/redis";
|
||||
|
||||
const ITEMNAME = 'silverbullet';
|
||||
|
||||
@@ -20,10 +21,6 @@ export default new Item({
|
||||
price: 666,
|
||||
execution: async (msg, user, specialargs) => {
|
||||
const messagequery = parseCommandArgs(msg.messageText, specialargs?.activation);
|
||||
if (!messagequery[0]) { await sendMessage('Please specify a target'); return; };
|
||||
const target = await User.initUsername(messagequery[0].toLowerCase());
|
||||
if (!target) { await sendMessage(`${messagequery[0]} doesn't exist`); return; };
|
||||
await getUserRecord(target); // make sure the user record exist in the database
|
||||
|
||||
if (await user.itemLock()) { await sendMessage('Cannot use an item (itemlock)', msg.messageId); return; };
|
||||
await user.setLock();
|
||||
@@ -31,6 +28,32 @@ export default new Item({
|
||||
const userObj = await getUserRecord(user);
|
||||
if (userObj.inventory[ITEMNAME]! < 1) { await sendMessage(`You don't have any silver bullets!`, msg.messageId); await user.clearLock(); return; };
|
||||
|
||||
let target: User | null;
|
||||
if (!messagequery[0]) {
|
||||
const vulnsids = await redis.keys('user:*:vulnerable');
|
||||
const baseusers = vulnsids.map(a => User.initUserId(a.slice(5, -11)));
|
||||
const users: User[] = [];
|
||||
for (const user of baseusers) {
|
||||
const a = await user;
|
||||
if (!a) continue;
|
||||
users.push(a);
|
||||
};
|
||||
if (users.length === 0) { await user.clearLock(); await sendMessage('No vulnerable chatters', msg.messageId); return; };
|
||||
target = users[Math.floor(Math.random() * users.length)]!;
|
||||
await playAlert({
|
||||
name: 'blastinRoulette',
|
||||
user: user.displayName,
|
||||
targets: users.map(a => a.displayName),
|
||||
finaltarget: target.displayName
|
||||
});
|
||||
await new Promise((res, _) => setTimeout(res, 6000));
|
||||
} else {
|
||||
target = await User.initUsername(messagequery[0].toLowerCase());
|
||||
};
|
||||
if (!target) { await user.clearLock(); await sendMessage(`${messagequery[0]} doesn't exist`); return; };
|
||||
|
||||
await getUserRecord(target); // make sure the user record exist in the database
|
||||
|
||||
const result = await timeout(target, `You got blasted by ${user.displayName}!`, 60 * 30);
|
||||
if (result.status) await Promise.all([
|
||||
sendMessage(`KEKPOINT KEKPOINT KEKPOINT ${target.displayName.toUpperCase()} RIPBOZO RIPBOZO RIPBOZO RIPBOZO RIPBOZO RIPBOZO RIPBOZO`),
|
||||
|
||||
@@ -27,12 +27,18 @@ export type soundAlert = alertBase<'sound'> & {
|
||||
sound: soundAlerts;
|
||||
};
|
||||
|
||||
export type blastinRoulette = alertBase<'blastinRoulette'> & {
|
||||
targets: string[];
|
||||
finaltarget?: string;
|
||||
};
|
||||
|
||||
export type alert =
|
||||
| userBlastAlert
|
||||
| userExecutionAlert
|
||||
| grenadeExplosionAlert
|
||||
| tntExplosionAlert
|
||||
| soundAlert;
|
||||
| soundAlert
|
||||
| blastinRoulette;
|
||||
|
||||
type playAlertEvent = {
|
||||
function: 'playAlert';
|
||||
|
||||
BIN
src/web/alerts/www/public/mariokartbox.ogg
Normal file
BIN
src/web/alerts/www/public/mariokartbox.ogg
Normal file
Binary file not shown.
110
src/web/alerts/www/src/alerts/blastinRoulette.ts
Normal file
110
src/web/alerts/www/src/alerts/blastinRoulette.ts
Normal file
@@ -0,0 +1,110 @@
|
||||
import { blastinRoulette } from "web/alerts/types";
|
||||
|
||||
function easeOutQuad(t: number) {
|
||||
return t * (2 - t);
|
||||
}
|
||||
|
||||
export default async function execute(alert: blastinRoulette) {
|
||||
const audio = new Audio("/alerts/public/mariokartbox.ogg");
|
||||
audio.play();
|
||||
const div = document.createElement('div');
|
||||
div.classList.add('blastin-roulette');
|
||||
|
||||
const words = (alert.targets && alert.targets.length) ? alert.targets.slice() : ["..."];
|
||||
|
||||
// how to center a div
|
||||
div.style.position = 'fixed';
|
||||
div.style.top = '50%';
|
||||
div.style.left = '50%';
|
||||
div.style.transform = 'translate(-50%, -50%)';
|
||||
div.style.display = 'flex';
|
||||
div.style.alignItems = 'center';
|
||||
div.style.justifyContent = 'center';
|
||||
|
||||
const text = document.createElement('span');
|
||||
text.style.fontFamily = '"Jersey 15"';
|
||||
text.style.fontSize = '6rem';
|
||||
text.style.fontWeight = '700';
|
||||
text.style.lineHeight = '1';
|
||||
text.style.textAlign = 'center';
|
||||
div.appendChild(text);
|
||||
|
||||
// animation parameters
|
||||
const totalDuration = 4000; // 4 sec
|
||||
const finalHold = 2000; // hold final word for 2 sec
|
||||
const steps = 60; // how many changes before settling
|
||||
|
||||
let lastPick = '';
|
||||
let chosenFinal: string | undefined;
|
||||
for (let i = 0; i < steps; i++) {
|
||||
const t = i / (steps - 1);
|
||||
const when = Math.round(easeOutQuad(t) * totalDuration);
|
||||
setTimeout(() => {
|
||||
if (i === steps - 1) {
|
||||
// If a finaltarget was provided, use it as the final word
|
||||
let pick: string;
|
||||
if (alert.finaltarget) pick = alert.finaltarget
|
||||
else {
|
||||
pick = words[Math.floor(Math.random() * words.length)];
|
||||
let attempts = 0;
|
||||
while (pick === lastPick && attempts < 10 && words.length > 1) {
|
||||
pick = words[Math.floor(Math.random() * words.length)];
|
||||
attempts++;
|
||||
};
|
||||
};
|
||||
text.textContent = pick;
|
||||
lastPick = pick;
|
||||
chosenFinal = pick;
|
||||
|
||||
// brief flicker effect: toggle opacity a few times
|
||||
const flickerTimes = 6;
|
||||
const flickerInterval = 80; // ms
|
||||
// ensure opacity starts at 1
|
||||
text.style.opacity = '1';
|
||||
for (let k = 0; k < flickerTimes; k++) {
|
||||
setTimeout(() => {
|
||||
text.style.opacity = (k % 2 === 0) ? '0' : '1';
|
||||
}, k * flickerInterval);
|
||||
};
|
||||
// ensure fully visible after flicker
|
||||
setTimeout(() => { text.style.opacity = '1'; }, flickerTimes * flickerInterval);
|
||||
} else {
|
||||
// pick a random word different from the previous shown
|
||||
let pick = words[Math.floor(Math.random() * words.length)];
|
||||
let attempts = 0;
|
||||
// Avoid showing the same word as lastPick
|
||||
while (pick === lastPick && attempts < 10 && words.length > 1) {
|
||||
pick = words[Math.floor(Math.random() * words.length)];
|
||||
attempts++;
|
||||
};
|
||||
// If the next step is the final and a finaltarget is provided, ensure the penultimate isn't equal to it
|
||||
if (i === steps - 2 && alert.finaltarget && pick === alert.finaltarget && words.length > 1) {
|
||||
attempts = 0;
|
||||
let fallback = words[Math.floor(Math.random() * words.length)];
|
||||
while ((fallback === pick || fallback === alert.finaltarget) && attempts < 10 && words.length > 1) {
|
||||
fallback = words[Math.floor(Math.random() * words.length)];
|
||||
attempts++;
|
||||
};
|
||||
pick = fallback;
|
||||
};
|
||||
|
||||
text.textContent = pick;
|
||||
lastPick = pick;
|
||||
};
|
||||
}, when);
|
||||
};
|
||||
|
||||
// resolve the completion promise after the final hold
|
||||
setTimeout(() => {
|
||||
// chosenFinal should be set by the final step; if not, pick one now
|
||||
if (!chosenFinal) {
|
||||
if (alert.finaltarget) chosenFinal = alert.finaltarget as string;
|
||||
else chosenFinal = words[Math.floor(Math.random() * words.length)];
|
||||
}
|
||||
}, totalDuration + finalHold);
|
||||
|
||||
// total time manager should wait/remove
|
||||
const duration = totalDuration + finalHold;
|
||||
|
||||
return { duration, alertDiv: div, blocking: true };
|
||||
};
|
||||
@@ -4,6 +4,7 @@ import userExecution from "./userExecution";
|
||||
import grenadeExplosion from "./grenadeExplosion";
|
||||
import tntExplosion from "./tntExplosion";
|
||||
import sound from "./sound";
|
||||
import blastinRoulette from "./blastinRoulette";
|
||||
|
||||
export type AlertRunner = {
|
||||
duration: number;
|
||||
@@ -20,5 +21,6 @@ export default {
|
||||
'userExecution': userExecution,
|
||||
'grenadeExplosion': grenadeExplosion,
|
||||
'tntExplosion': tntExplosion,
|
||||
'sound': sound
|
||||
'sound': sound,
|
||||
'blastinRoulette': blastinRoulette
|
||||
} as AlertMap;
|
||||
|
||||
Reference in New Issue
Block a user