button reward updates

This commit is contained in:
legop3
2026-04-24 13:19:56 -04:00
parent d3f263c216
commit 0c93aacdf6
5 changed files with 88 additions and 43 deletions
@@ -1,5 +1,6 @@
const STEP_MS = 220; const STEP_MS = 220;
const STEPS = 40; const DURATION_MS = 30 * 1000;
const STEPS = Math.ceil(DURATION_MS / STEP_MS);
module.exports = { module.exports = {
id: 'cameraWhiplash', id: 'cameraWhiplash',
@@ -29,13 +30,23 @@ module.exports = {
}); });
if (step >= STEPS) { if (step >= STEPS) {
clearInterval(timer); clearInterval(timer);
rovers.forEach((rover) => {
try {
ctx.issueCommand(String(rover.id), {
type: 'servo',
servo: { angle: 0 },
});
} catch (err) {
ctx.logger.warn('cameraWhiplash servo reset failed', { roverId: rover.id, error: err.message });
}
});
} }
}, STEP_MS); }, STEP_MS);
ctx.sendAlert({ ctx.sendAlert({
color: '#00bcd4', color: '#00bcd4',
title: 'Camera Wiggle', title: 'Camera Wiggle',
message: `Wobble running on ${rovers.length} rover(s).`, message: `Wobble running for 30 seconds on ${rovers.length} rover(s).`,
}); });
}, },
}; };
+32 -16
View File
@@ -1,32 +1,48 @@
const LETTERS = 'abcdefghijklmnopqrstuvwxyz'; const LETTERS = 'abcdefghijklmnopqrstuvwxyz';
const BURST_COUNT = 18;
const MIN_BURST_SIZE = 2;
const MAX_BURST_SIZE = 8;
const MIN_BURST_DELAY_MS = 80;
const MAX_BURST_DELAY_MS = 420;
function randLetter() { function randLetter() {
return LETTERS[Math.floor(Math.random() * LETTERS.length)]; return LETTERS[Math.floor(Math.random() * LETTERS.length)];
} }
function randInt(min, max) {
return min + Math.floor(Math.random() * (max - min + 1));
}
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
module.exports = { module.exports = {
id: 'chatSpam', id: 'chatSpam',
name: 'Chat Spam', name: 'Chat Spam',
goal: 320, goal: 320,
async run(ctx) { async run(ctx) {
const nickname = randLetter(); const nickname = randLetter();
const messageCount = 100; for (let burst = 0; burst < BURST_COUNT; burst += 1) {
for (let i = 0; i < messageCount; i += 1) { const burstSize = randInt(MIN_BURST_SIZE, MAX_BURST_SIZE);
const len = 3 + Math.floor(Math.random() * 14); for (let i = 0; i < burstSize; i += 1) {
let text = ''; const len = 3 + Math.floor(Math.random() * 14);
for (let j = 0; j < len; j += 1) { let text = '';
text += randLetter(); for (let j = 0; j < len; j += 1) {
} text += randLetter();
try { }
ctx.sendExternalMessage({ try {
nickname, ctx.sendExternalMessage({
role: 'spectator', nickname,
roverId: null, role: 'spectator',
text, roverId: null,
}); text,
} catch { });
// ignore spam errors } catch {
// ignore spam errors
}
} }
await sleep(randInt(MIN_BURST_DELAY_MS, MAX_BURST_DELAY_MS));
} }
}, },
}; };
@@ -1,16 +1,23 @@
const NAMES = ['ross', 'david', 'chirpet', 'caydu', 'meow', 'wawa']; const NAMES = ['ross', 'david', 'chirpet', 'caydu', 'meow', 'wawa'];
const BURSTS = 200; const BURSTS = 120;
const TICK_MS = 180; const MIN_BURST_DELAY_MS = 70;
const MAX_BURST_DELAY_MS = 350;
function randInt(min, max) {
return min + Math.floor(Math.random() * (max - min + 1));
}
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
module.exports = { module.exports = {
id: 'ghostTypingSpam', id: 'ghostTypingSpam',
name: 'Typing Spam', name: 'Typing Spam',
goal: 220, goal: 220,
async run(ctx) { async run(ctx) {
let tick = 0;
const active = new Set(); const active = new Set();
const timer = setInterval(() => { for (let burst = 0; burst < BURSTS; burst += 1) {
tick += 1;
const name = NAMES[Math.floor(Math.random() * NAMES.length)] + String(Math.floor(Math.random() * 10)); const name = NAMES[Math.floor(Math.random() * NAMES.length)] + String(Math.floor(Math.random() * 10));
const on = Math.random() > 0.35; const on = Math.random() > 0.35;
try { try {
@@ -24,17 +31,16 @@ module.exports = {
} catch { } catch {
// ignore // ignore
} }
if (tick >= BURSTS) { await sleep(randInt(MIN_BURST_DELAY_MS, MAX_BURST_DELAY_MS));
clearInterval(timer); }
active.forEach((ghost) => {
try { active.forEach((ghost) => {
ctx.sendExternalTyping({ nickname: ghost, role: 'spectator', roverId: null, isTyping: false }); try {
} catch { ctx.sendExternalTyping({ nickname: ghost, role: 'spectator', roverId: null, isTyping: false });
// ignore } catch {
} // ignore
});
} }
}, TICK_MS); });
ctx.sendAlert({ color: '#9c27b0', title: 'Typing Spam', message: 'Ghost typing burst started.' }); ctx.sendAlert({ color: '#9c27b0', title: 'Typing Spam', message: 'Ghost typing burst started.' });
}, },
+10 -6
View File
@@ -1,4 +1,5 @@
const DURATION_MS = 300 * 1000; const MIN_DURATION_MS = 5 * 60 * 1000;
const MAX_DURATION_MS = 10 * 60 * 1000;
let activeTimer = null; let activeTimer = null;
@@ -36,7 +37,7 @@ async function restore(ctx, effect = {}) {
function scheduleRestore(ctx, effect = {}) { function scheduleRestore(ctx, effect = {}) {
clearTimer(); clearTimer();
const endsAt = Number(effect.endsAt || Date.now() + DURATION_MS); const endsAt = Number(effect.endsAt || Date.now() + MIN_DURATION_MS);
const remaining = Math.max(0, endsAt - Date.now()); const remaining = Math.max(0, endsAt - Date.now());
const next = { ...effect, endsAt }; const next = { ...effect, endsAt };
ctx.saveEffect('modeJam', next); ctx.saveEffect('modeJam', next);
@@ -49,12 +50,15 @@ function scheduleRestore(ctx, effect = {}) {
module.exports = { module.exports = {
id: 'modeJam', id: 'modeJam',
name: 'Admin Mode', name: 'Admin Lock',
goal: 1000, goal: 900,
async run(ctx) { async run(ctx) {
const durationMs =
MIN_DURATION_MS + Math.floor(Math.random() * (MAX_DURATION_MS - MIN_DURATION_MS + 1));
const endsAt = Date.now() + durationMs;
const prevMode = ctx.getMode(); const prevMode = ctx.getMode();
const prevReason = ctx.getAdminReasonText(); const prevReason = ctx.getAdminReasonText();
const jamReason = `Locked by button box until ${new Date(Date.now() + DURATION_MS).toLocaleTimeString()}`; const jamReason = `Locked by button box until ${new Date(endsAt).toLocaleTimeString()}`;
try { try {
ctx.setMode('admin', 'buttonbox:modeJam'); ctx.setMode('admin', 'buttonbox:modeJam');
@@ -70,7 +74,7 @@ module.exports = {
scheduleRestore(ctx, { scheduleRestore(ctx, {
prevMode, prevMode,
prevReason, prevReason,
endsAt: Date.now() + DURATION_MS, endsAt,
}); });
ctx.sendAlert({ color: '#ff5722', title: 'Admin Mode', message: 'Server forced into admin mode temporarily.' }); ctx.sendAlert({ color: '#ff5722', title: 'Admin Mode', message: 'Server forced into admin mode temporarily.' });
+12 -4
View File
@@ -129,9 +129,10 @@ function ensureRewardAssignments() {
}); });
} }
function assignNewReward(button) { function assignNewReward(button, options = {}) {
const allRewards = listRewards(); const allRewards = listRewards();
if (!allRewards.length) throw new Error('No rewards configured'); if (!allRewards.length) throw new Error('No rewards configured');
const excludeRewardId = typeof options.excludeRewardId === 'string' ? options.excludeRewardId : null;
const usedByOtherButtons = new Set( const usedByOtherButtons = new Set(
state.buttons state.buttons
@@ -139,8 +140,15 @@ function assignNewReward(button) {
.map((entry) => entry.rewardId) .map((entry) => entry.rewardId)
.filter((rewardId) => typeof rewardId === 'string' && rewardId.length > 0), .filter((rewardId) => typeof rewardId === 'string' && rewardId.length > 0),
); );
const uniqueCandidates = allRewards.filter((reward) => !usedByOtherButtons.has(reward.id)); const uniqueCandidates = allRewards.filter(
const pool = uniqueCandidates.length ? uniqueCandidates : allRewards; (reward) => !usedByOtherButtons.has(reward.id) && (!excludeRewardId || reward.id !== excludeRewardId),
);
const fallbackUniqueCandidates = allRewards.filter((reward) => !usedByOtherButtons.has(reward.id));
const pool = uniqueCandidates.length
? uniqueCandidates
: fallbackUniqueCandidates.length
? fallbackUniqueCandidates
: allRewards.filter((reward) => !excludeRewardId || reward.id !== excludeRewardId);
const reward = pool[Math.floor(Math.random() * pool.length)] || null; const reward = pool[Math.floor(Math.random() * pool.length)] || null;
if (!reward) throw new Error('No rewards configured'); if (!reward) throw new Error('No rewards configured');
@@ -260,7 +268,7 @@ async function runRewardForButton(button) {
}); });
button.lastRewardAt = Date.now(); button.lastRewardAt = Date.now();
button.count = 0; button.count = 0;
assignNewReward(button); assignNewReward(button, { excludeRewardId: reward.id });
} }
async function applyPress(buttonId) { async function applyPress(buttonId) {