mirror of
https://github.com/qwerinope/qweribot.git
synced 2025-12-19 01:01:39 +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
|
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
|
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
|
TNT|`tnt`|Give 5-10 random chatters 60 second timeouts|`tnt`|1000
|
||||||
|
|
||||||
## Cheers
|
## 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
|
`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
|
`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).
|
`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
|
`tnt`|1000|`cheer1000`|Gives 5-10 random vulnerable chatters 60 second timeouts. On failure gives cheerer a TNT
|
||||||
|
|
||||||
## Point Redeems
|
## Point Redeems
|
||||||
|
|||||||
@@ -7,13 +7,35 @@ import { createTimeoutRecord } from "db/dbTimeouts";
|
|||||||
import { parseCheerArgs } from "lib/parseCommandArgs";
|
import { parseCheerArgs } from "lib/parseCommandArgs";
|
||||||
import { createCheerEventRecord } from "db/dbCheerEvents";
|
import { createCheerEventRecord } from "db/dbCheerEvents";
|
||||||
import { playAlert } from "web/alerts/serverFunctions";
|
import { playAlert } from "web/alerts/serverFunctions";
|
||||||
|
import { redis } from "lib/redis";
|
||||||
|
|
||||||
const ITEMNAME = 'silverbullet';
|
const ITEMNAME = 'silverbullet';
|
||||||
|
|
||||||
export default new Cheer('execute', 666, async (msg, user) => {
|
export default new Cheer('execute', 666, async (msg, user) => {
|
||||||
const args = parseCheerArgs(msg.messageText);
|
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; };
|
if (!target) { await handleNoTarget(msg, user, ITEMNAME, false); return; };
|
||||||
await getUserRecord(target);
|
await getUserRecord(target);
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import parseCommandArgs from "lib/parseCommandArgs";
|
|||||||
import { timeout } from "lib/timeout";
|
import { timeout } from "lib/timeout";
|
||||||
import { playAlert } from "web/alerts/serverFunctions";
|
import { playAlert } from "web/alerts/serverFunctions";
|
||||||
import User from "user";
|
import User from "user";
|
||||||
|
import { redis } from "lib/redis";
|
||||||
|
|
||||||
const ITEMNAME = 'silverbullet';
|
const ITEMNAME = 'silverbullet';
|
||||||
|
|
||||||
@@ -20,10 +21,6 @@ export default new Item({
|
|||||||
price: 666,
|
price: 666,
|
||||||
execution: async (msg, user, specialargs) => {
|
execution: async (msg, user, specialargs) => {
|
||||||
const messagequery = parseCommandArgs(msg.messageText, specialargs?.activation);
|
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; };
|
if (await user.itemLock()) { await sendMessage('Cannot use an item (itemlock)', msg.messageId); return; };
|
||||||
await user.setLock();
|
await user.setLock();
|
||||||
@@ -31,6 +28,32 @@ export default new Item({
|
|||||||
const userObj = await getUserRecord(user);
|
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; };
|
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);
|
const result = await timeout(target, `You got blasted by ${user.displayName}!`, 60 * 30);
|
||||||
if (result.status) await Promise.all([
|
if (result.status) await Promise.all([
|
||||||
sendMessage(`KEKPOINT KEKPOINT KEKPOINT ${target.displayName.toUpperCase()} RIPBOZO RIPBOZO RIPBOZO RIPBOZO RIPBOZO RIPBOZO RIPBOZO`),
|
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;
|
sound: soundAlerts;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type blastinRoulette = alertBase<'blastinRoulette'> & {
|
||||||
|
targets: string[];
|
||||||
|
finaltarget?: string;
|
||||||
|
};
|
||||||
|
|
||||||
export type alert =
|
export type alert =
|
||||||
| userBlastAlert
|
| userBlastAlert
|
||||||
| userExecutionAlert
|
| userExecutionAlert
|
||||||
| grenadeExplosionAlert
|
| grenadeExplosionAlert
|
||||||
| tntExplosionAlert
|
| tntExplosionAlert
|
||||||
| soundAlert;
|
| soundAlert
|
||||||
|
| blastinRoulette;
|
||||||
|
|
||||||
type playAlertEvent = {
|
type playAlertEvent = {
|
||||||
function: 'playAlert';
|
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 grenadeExplosion from "./grenadeExplosion";
|
||||||
import tntExplosion from "./tntExplosion";
|
import tntExplosion from "./tntExplosion";
|
||||||
import sound from "./sound";
|
import sound from "./sound";
|
||||||
|
import blastinRoulette from "./blastinRoulette";
|
||||||
|
|
||||||
export type AlertRunner = {
|
export type AlertRunner = {
|
||||||
duration: number;
|
duration: number;
|
||||||
@@ -20,5 +21,6 @@ export default {
|
|||||||
'userExecution': userExecution,
|
'userExecution': userExecution,
|
||||||
'grenadeExplosion': grenadeExplosion,
|
'grenadeExplosion': grenadeExplosion,
|
||||||
'tntExplosion': tntExplosion,
|
'tntExplosion': tntExplosion,
|
||||||
'sound': sound
|
'sound': sound,
|
||||||
|
'blastinRoulette': blastinRoulette
|
||||||
} as AlertMap;
|
} as AlertMap;
|
||||||
|
|||||||
Reference in New Issue
Block a user